Compare commits
86 Commits
821b6b47f3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 085cfd8ba2 | |||
| 14d827ab66 | |||
| aa30431df8 | |||
| a127535b36 | |||
| aa17ea99a2 | |||
| ae24be2c22 | |||
| 0b9f886905 | |||
| 7ecd8094e2 | |||
| 9718713eb4 | |||
| 36a5900118 | |||
| 1eebfc90fa | |||
| 7b2962be15 | |||
| c738fe8d50 | |||
| 76fdc4be43 | |||
| ff3fac2ab9 | |||
| 62f976b6f3 | |||
| 00a06a1ca9 | |||
| 6800256f09 | |||
| 17b998ee60 | |||
| 87a991eaea | |||
| 9ce25e73f0 | |||
| 6cad199431 | |||
| 36f9057a30 | |||
| 05c77f30bd | |||
| 739c593d2a | |||
| 034d02f042 | |||
| c2685a2731 | |||
| 28c6f79fdb | |||
| 77a1c51692 | |||
| 319c288586 | |||
| 980035e3d1 | |||
| 520df5b570 | |||
| fdd0ada815 | |||
| 8092846d6f | |||
| 7b58b65db3 | |||
| d0c33c31a8 | |||
| 313dab26a2 | |||
| 7ead18601c | |||
| c80e8d1c67 | |||
| d4bc73e32c | |||
| 6ee4c8efc7 | |||
| badae8a3a0 | |||
| c783cbe543 | |||
| cf5ae33ba4 | |||
| a3d217bbdd | |||
| 7cad5fbdf9 | |||
| fe8a8034ca | |||
| 138ce6ec8b | |||
| 690fd6d831 | |||
| 24fd224ec6 | |||
| c9ce785f12 | |||
| e0fc62df84 | |||
| 8a590aa2fc | |||
| 3fd68fa938 | |||
| 3ba7b2bfab | |||
| b44d2960f7 | |||
| c5529d5f94 | |||
| f63f5c240e | |||
| 0bb7ae2361 | |||
| 7f74263ba2 | |||
| c43539b640 | |||
| 93a72c628e | |||
| 39ddd1cf95 | |||
| 6f11c827c6 | |||
| b5de606a3e | |||
| 837c4a327b | |||
| 5b365f1eab | |||
| dfa4f3239c | |||
| 1c69ae56ac | |||
| 9adfa18e9b | |||
| a2e7b235fe | |||
| 49f37465bd | |||
| c3835f45c5 | |||
| 17fe2330c6 | |||
| b88fb4ca9d | |||
| daca0a8294 | |||
| e80e3ff3f3 | |||
| 8e17db7a2e | |||
| 238001ae93 | |||
| b7956766e8 | |||
| ca1183d88a | |||
| 4c34994ac7 | |||
| 8385c85820 | |||
| 71e9c1a45a | |||
| 880bbc3d9a | |||
| e293193dd7 |
60
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
name: Deploy Spot
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: spot
|
||||||
|
|
||||||
|
env:
|
||||||
|
COMPOSER_NO_INTERACTION: "1"
|
||||||
|
COMPOSER_HOME: .composer
|
||||||
|
DEPLOY_PATH: /var/www/spot
|
||||||
|
npm_config_cache: .npm-cache
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check runner tools
|
||||||
|
run: |
|
||||||
|
command -v composer
|
||||||
|
command -v npm
|
||||||
|
command -v rsync
|
||||||
|
|
||||||
|
- name: Check deploy path
|
||||||
|
run: |
|
||||||
|
test -d "$DEPLOY_PATH"
|
||||||
|
test -w "$DEPLOY_PATH"
|
||||||
|
|
||||||
|
- name: Validate Composer configuration
|
||||||
|
run: composer validate --no-check-publish
|
||||||
|
|
||||||
|
- name: Install PHP dependencies
|
||||||
|
run: composer install --no-dev --prefer-dist --optimize-autoloader
|
||||||
|
|
||||||
|
- name: Install npm dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Prepare runtime missing mount points
|
||||||
|
run: mkdir -p files resources/geo
|
||||||
|
|
||||||
|
- name: Build frontend
|
||||||
|
run: npm run prod
|
||||||
|
|
||||||
|
- name: Deploy to production
|
||||||
|
run: |
|
||||||
|
rsync -azc --no-times --delete \
|
||||||
|
--exclude "/.git/" \
|
||||||
|
--exclude "/.gitea/" \
|
||||||
|
--exclude "/.composer/" \
|
||||||
|
--exclude "/.npm-cache/" \
|
||||||
|
--exclude "/node_modules/" \
|
||||||
|
--exclude "/config/settings.php" \
|
||||||
|
--exclude "/log.html" \
|
||||||
|
--exclude "/files/" \
|
||||||
|
--exclude "/resources/geo/*.geojson" \
|
||||||
|
./ "$DEPLOY_PATH/"
|
||||||
20
.gitignore
vendored
@@ -1,8 +1,16 @@
|
|||||||
/vendor/
|
# App config files
|
||||||
/config/settings.php
|
/config/settings.php
|
||||||
/files/
|
|
||||||
/geo/
|
|
||||||
/node_modules/
|
|
||||||
/log.html
|
/log.html
|
||||||
/dist/
|
|
||||||
.codex
|
# Upload folders
|
||||||
|
/files/
|
||||||
|
/resources/geo/*.geojson
|
||||||
|
|
||||||
|
# Build folder
|
||||||
|
/public/*
|
||||||
|
!/public/index.php
|
||||||
|
|
||||||
|
# Dependencies files
|
||||||
|
/vendor/
|
||||||
|
/node_modules/
|
||||||
|
/composer.dev.lock
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
|
||||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
|
||||||
const SymlinkWebpackPlugin = require('symlink-webpack-plugin');
|
const SymlinkWebpackPlugin = require('symlink-webpack-plugin');
|
||||||
const { VueLoaderPlugin } = require('vue-loader');
|
const { VueLoaderPlugin } = require('vue-loader');
|
||||||
|
|
||||||
const ROOT = path.resolve(__dirname, '..');
|
const ROOT = path.resolve(__dirname, '..');
|
||||||
const SRC = path.resolve(ROOT, 'src');
|
const SRC = path.resolve(ROOT, 'src');
|
||||||
const DIST = path.resolve(ROOT, 'dist');
|
const PUBLIC = path.resolve(ROOT, 'public');
|
||||||
const LIB = path.resolve(ROOT, 'lib');
|
|
||||||
|
|
||||||
module.exports = (env, argv) => {
|
module.exports = (env, argv) => {
|
||||||
const mode = argv.mode || 'production';
|
const mode = argv.mode || 'production';
|
||||||
@@ -22,9 +20,39 @@ module.exports = (env, argv) => {
|
|||||||
app: path.resolve(SRC, 'app.js')
|
app: path.resolve(SRC, 'app.js')
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: DIST,
|
path: PUBLIC,
|
||||||
filename: '[name].js',
|
filename: isDev ? 'assets/[name].js' : 'assets/[name].[contenthash:8].js',
|
||||||
publicPath: '../dist/'
|
chunkFilename: isDev ? 'assets/[name].js' : 'assets/[name].[contenthash:8].js',
|
||||||
|
publicPath: './',
|
||||||
|
clean: isDev ? false : {
|
||||||
|
keep: /^(index\.php|files|geo|assets\/images\/icons)(\/.*)?$/
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'all',
|
||||||
|
cacheGroups: {
|
||||||
|
maplibre: {
|
||||||
|
test: /[\\/]node_modules[\\/]maplibre-gl[\\/]/,
|
||||||
|
name: 'maplibre',
|
||||||
|
chunks: 'all',
|
||||||
|
priority: 30,
|
||||||
|
enforce: true
|
||||||
|
},
|
||||||
|
uppy: {
|
||||||
|
test: /[\\/]node_modules[\\/](@uppy|@transloadit|namespace-emitter|nanoid)[\\/]/,
|
||||||
|
name: 'uppy',
|
||||||
|
chunks: 'async',
|
||||||
|
priority: 20,
|
||||||
|
enforce: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
performance: {
|
||||||
|
hints: isDev ? false : 'warning',
|
||||||
|
maxEntrypointSize: 1500 * 1024,
|
||||||
|
maxAssetSize: 1100 * 1024
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [{
|
rules: [{
|
||||||
@@ -39,9 +67,6 @@ module.exports = (env, argv) => {
|
|||||||
presets: ['@babel/preset-env']
|
presets: ['@babel/preset-env']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
test: /\.html$/i,
|
|
||||||
loader: 'html-loader'
|
|
||||||
}, {
|
}, {
|
||||||
test: /\.s[ac]ss$/i,
|
test: /\.s[ac]ss$/i,
|
||||||
use: [
|
use: [
|
||||||
@@ -50,7 +75,7 @@ module.exports = (env, argv) => {
|
|||||||
{
|
{
|
||||||
loader: 'sass-loader',
|
loader: 'sass-loader',
|
||||||
options: {
|
options: {
|
||||||
implementation: require.resolve('sass'),
|
implementation: require('sass'),
|
||||||
sourceMap: isDev
|
sourceMap: isDev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +84,7 @@ module.exports = (env, argv) => {
|
|||||||
test: /\.css$/i,
|
test: /\.css$/i,
|
||||||
use: ['vue-style-loader', 'css-loader']
|
use: ['vue-style-loader', 'css-loader']
|
||||||
}, {
|
}, {
|
||||||
test: /\.(png|svg|jpg|jpeg|gif)$/i,
|
test: /\.(png|svg|jpg|jpeg|gif|webp)$/i,
|
||||||
type: 'asset',
|
type: 'asset',
|
||||||
parser: {
|
parser: {
|
||||||
dataUrlCondition: {
|
dataUrlCondition: {
|
||||||
@@ -67,38 +92,62 @@ module.exports = (env, argv) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
generator: {
|
generator: {
|
||||||
filename: 'images/[name][ext]'
|
filename: isDev ? 'assets/images/[name][ext]' : 'assets/images/[name].[contenthash:8][ext]'
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyWebpackPlugin({
|
|
||||||
patterns: [
|
|
||||||
{ from: path.resolve(LIB, 'index.php'), to: 'index.php' },
|
|
||||||
{ from: path.resolve(SRC, 'images', 'logo_black.png'), to: 'images' },
|
|
||||||
{ from: path.resolve(SRC, 'images', 'spot-logo-only.svg'), to: 'images' }
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
new SymlinkWebpackPlugin([
|
new SymlinkWebpackPlugin([
|
||||||
{ origin: '../files/', symlink: 'files' },
|
{ origin: '../files/', symlink: 'files' },
|
||||||
{ origin: '../geo/', symlink: 'geo' },
|
{ origin: '../resources/geo/', symlink: 'geo' },
|
||||||
{ origin: '../src/images/icons/', symlink: 'images/icons' }
|
{ origin: '../src/images/icons/', symlink: 'assets/images/icons' }
|
||||||
]),
|
]),
|
||||||
new CleanWebpackPlugin(),
|
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
__VUE_OPTIONS_API__: 'true',
|
__VUE_OPTIONS_API__: 'true',
|
||||||
__VUE_PROD_DEVTOOLS__: 'false',
|
__VUE_PROD_DEVTOOLS__: 'false',
|
||||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false'
|
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false'
|
||||||
}),
|
}),
|
||||||
|
{
|
||||||
|
apply(compiler) {
|
||||||
|
compiler.hooks.done.tap('EntryPointManifestPlugin', (stats) => {
|
||||||
|
const manifest = {
|
||||||
|
entrypoints: mapChunkGroups(stats.compilation.entrypoints),
|
||||||
|
chunkGroups: mapChunkGroups(stats.compilation.chunkGroups)
|
||||||
|
};
|
||||||
|
|
||||||
|
const manifestPath = path.resolve(PUBLIC, 'assets', 'entrypoints.json');
|
||||||
|
const tmpManifestPath = `${manifestPath}.tmp`;
|
||||||
|
|
||||||
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
||||||
|
fs.writeFileSync(tmpManifestPath, JSON.stringify(manifest, null, '\t'));
|
||||||
|
fs.renameSync(tmpManifestPath, manifestPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
new VueLoaderPlugin()
|
new VueLoaderPlugin()
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.vue', '.scss', '...'],
|
extensions: ['.vue', '.scss', '...'],
|
||||||
alias: {
|
alias: {
|
||||||
'@components': path.resolve(SRC, 'components'),
|
'@components': path.resolve(SRC, 'components'),
|
||||||
|
'@images': path.resolve(SRC, 'images'),
|
||||||
'@scripts': path.resolve(SRC, 'scripts'),
|
'@scripts': path.resolve(SRC, 'scripts'),
|
||||||
'@styles': path.resolve(SRC, 'styles')
|
'@styles': path.resolve(SRC, 'styles')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function mapChunkGroups(chunkGroups = {}) {
|
||||||
|
const chunkGroupEntries = (chunkGroups instanceof Map)?Array.from(chunkGroups.entries()):Array.from(chunkGroups).map((chunkGroup) => [chunkGroup.name, chunkGroup]);
|
||||||
|
return Object.fromEntries(
|
||||||
|
chunkGroupEntries
|
||||||
|
.filter(([name]) => name)
|
||||||
|
.map(([name, chunkGroup]) => [
|
||||||
|
name,
|
||||||
|
Array.from((typeof chunkGroup.getFiles === 'function')?chunkGroup.getFiles():chunkGroup.assets)
|
||||||
|
.map((asset) => (typeof asset === 'string')?asset:asset.name)
|
||||||
|
.filter((file) => file.endsWith('.js'))
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
5
cli/cron.sh
Normal file → Executable file
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
wget -qO- https://spot.lutran.fr/index.php?a=update_project > /dev/null
|
cd "$(dirname "$0")/../public" || exit 1 #Execute from public folder
|
||||||
|
php -f index.php a=update_project > /dev/null
|
||||||
|
|
||||||
#Crontab job: 0 * * * * . /var/www/spot/spot_cron.sh > /dev/null
|
#Crontab job: 0 * * * * /path/to/spot/cli/cron.sh > /dev/null
|
||||||
|
|||||||
28
composer.dev.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "franzz/spot",
|
||||||
|
"description": "LiveTrail",
|
||||||
|
"type": "project",
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "path",
|
||||||
|
"url": "../objects",
|
||||||
|
"options": {
|
||||||
|
"symlink": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"franzz/objects": "dev-vue",
|
||||||
|
"phpmailer/phpmailer": "^7.1"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Franzz\\Spot\\": "lib/",
|
||||||
|
"Franzz\\Objects\\": "../objects/inc/"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/settings.php"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "franzz/spot",
|
"name": "franzz/spot",
|
||||||
"description": "Spotty",
|
"description": "LiveTrail",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
"repositories": [
|
"repositories": [
|
||||||
{
|
{
|
||||||
"type": "path",
|
"type": "git",
|
||||||
"url": "../objects"
|
"url": "https://git.lutran.fr/franzz/objects"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"franzz/objects": "dev-vue",
|
"franzz/objects": "dev-vue",
|
||||||
"phpmailer/phpmailer": "^6.5"
|
"phpmailer/phpmailer": "^7.1"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|||||||
35
composer.lock
generated
@@ -4,15 +4,15 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "12bb836a394b645df50c14652a2ae5bf",
|
"content-hash": "8eb764990f0cb9427030c2bf01390d62",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "franzz/objects",
|
"name": "franzz/objects",
|
||||||
"version": "dev-vue",
|
"version": "dev-vue",
|
||||||
"dist": {
|
"source": {
|
||||||
"type": "path",
|
"type": "git",
|
||||||
"url": "../objects",
|
"url": "https://git.lutran.fr/franzz/objects",
|
||||||
"reference": "bcae723140735b1432caaf3070ef4e29ecb73a76"
|
"reference": "af7d0f4c86564995f1c8149df98a183d33fab767"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -21,22 +21,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"description": "Objects",
|
"description": "Objects",
|
||||||
"transport-options": {
|
"time": "2026-05-29T23:29:34+00:00"
|
||||||
"relative": true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpmailer/phpmailer",
|
"name": "phpmailer/phpmailer",
|
||||||
"version": "v6.12.0",
|
"version": "v7.1.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||||
"reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12"
|
"reference": "1bc1716a507a65e039d4ac9d9adebbbd0d346e15"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/d1ac35d784bf9f5e61b424901d5a014967f15b12",
|
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/1bc1716a507a65e039d4ac9d9adebbbd0d346e15",
|
||||||
"reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12",
|
"reference": "1bc1716a507a65e039d4ac9d9adebbbd0d346e15",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -50,13 +48,14 @@
|
|||||||
"doctrine/annotations": "^1.2.6 || ^1.13.3",
|
"doctrine/annotations": "^1.2.6 || ^1.13.3",
|
||||||
"php-parallel-lint/php-console-highlighter": "^1.0.0",
|
"php-parallel-lint/php-console-highlighter": "^1.0.0",
|
||||||
"php-parallel-lint/php-parallel-lint": "^1.3.2",
|
"php-parallel-lint/php-parallel-lint": "^1.3.2",
|
||||||
"phpcompatibility/php-compatibility": "^9.3.5",
|
"phpcompatibility/php-compatibility": "^10.0.0@dev",
|
||||||
"roave/security-advisories": "dev-latest",
|
"squizlabs/php_codesniffer": "^3.13.5",
|
||||||
"squizlabs/php_codesniffer": "^3.7.2",
|
|
||||||
"yoast/phpunit-polyfills": "^1.0.4"
|
"yoast/phpunit-polyfills": "^1.0.4"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
|
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
|
||||||
|
"directorytree/imapengine": "For uploading sent messages via IMAP, see gmail example",
|
||||||
|
"ext-imap": "Needed to support advanced email address parsing according to RFC822",
|
||||||
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
|
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
|
||||||
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
|
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
|
||||||
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
|
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
|
||||||
@@ -96,7 +95,7 @@
|
|||||||
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.12.0"
|
"source": "https://github.com/PHPMailer/PHPMailer/tree/v7.1.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -104,7 +103,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-10-15T16:49:08+00:00"
|
"time": "2026-05-18T08:06:14+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [],
|
"packages-dev": [],
|
||||||
@@ -117,5 +116,5 @@
|
|||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {},
|
"platform": {},
|
||||||
"platform-dev": {},
|
"platform-dev": {},
|
||||||
"plugin-api-version": "2.6.0"
|
"plugin-api-version": "2.9.0"
|
||||||
}
|
}
|
||||||
|
|||||||
16
config/apache-localhost.conf
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName localhost
|
||||||
|
ServerAlias maui.local
|
||||||
|
DocumentRoot /var/www/html/
|
||||||
|
|
||||||
|
DirectoryIndex index.php
|
||||||
|
|
||||||
|
# Serve http://localhost/spot/ from the public web root.
|
||||||
|
Alias /spot /var/www/html/spot/public
|
||||||
|
|
||||||
|
<Directory /var/www/html/spot/public>
|
||||||
|
Options FollowSymLinks
|
||||||
|
AllowOverride None
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
</VirtualHost>
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
CREATE TABLE `maps` (
|
|
||||||
`id_map` int(10) UNSIGNED auto_increment,
|
|
||||||
`codename` VARCHAR(100),
|
|
||||||
`geo_name` VARCHAR(100),
|
|
||||||
`min_zoom` TINYINT UNSIGNED,
|
|
||||||
`max_zoom` TINYINT UNSIGNED,
|
|
||||||
`attribution` VARCHAR(100),
|
|
||||||
`led` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id_map`));
|
|
||||||
|
|
||||||
CREATE TABLE `mappings` (
|
|
||||||
`id_mapping` int(10) UNSIGNED auto_increment,
|
|
||||||
`id_map` int(10) UNSIGNED,
|
|
||||||
`id_project` int(10) UNSIGNED,
|
|
||||||
`led` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id_mapping`));
|
|
||||||
|
|
||||||
ALTER TABLE mappings ADD INDEX(`id_map`);
|
|
||||||
ALTER TABLE mappings ADD FOREIGN KEY (`id_map`) REFERENCES maps(`id_map`);
|
|
||||||
ALTER TABLE mappings ADD INDEX(`id_project`);
|
|
||||||
ALTER TABLE mappings ADD FOREIGN KEY (`id_project`) REFERENCES projects(`id_project`);
|
|
||||||
|
|
||||||
INSERT INTO maps(codename, geo_name, min_zoom, max_zoom, attribution) VALUES
|
|
||||||
/*1*/('satellite', 'mapbox.satellite-streets', 0, 19, ''),
|
|
||||||
/*2*/('otm', 'opentopomap', 2, 19, ''),
|
|
||||||
/*3*/('ign_france', 'ign.fr', 2, 19, ''),
|
|
||||||
/*4*/('ign_spain', 'ign.es', 1, 20, ''),
|
|
||||||
/*5*/('linz', 'linz', 0, 17, 'Sourced from LINZ. CC BY 4.0'),
|
|
||||||
/*6*/('usgs', 'usgs', 1, 16, ''),
|
|
||||||
/*7*/('natgeo', 'natgeo.pct', 5, 14, '');
|
|
||||||
|
|
||||||
INSERT INTO mappings(id_map, id_project) VALUES
|
|
||||||
(1, NULL),
|
|
||||||
(2, NULL),
|
|
||||||
(3, 2),
|
|
||||||
(4, 2),
|
|
||||||
(5, 1),
|
|
||||||
(6, 3);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE users ADD gravatar LONGTEXT AFTER email;
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
ALTER TABLE medias ADD timezone CHAR(64) AFTER posted_on;
|
|
||||||
ALTER TABLE messages ADD timezone CHAR(64) AFTER site_time;
|
|
||||||
ALTER TABLE posts ADD timezone CHAR(64) AFTER site_time;
|
|
||||||
|
|
||||||
UPDATE messages
|
|
||||||
SET iso_time = DATE_FORMAT(CONVERT_TZ(LEFT(iso_time, 19),'+00:00','+02:00'), '%Y-%m-%dT%T+0200'),
|
|
||||||
led = led
|
|
||||||
WHERE id_feed = 2;
|
|
||||||
|
|
||||||
UPDATE messages
|
|
||||||
INNER JOIN feeds ON feeds.id_feed = messages.id_feed
|
|
||||||
INNER JOIN projects ON projects.id_project = feeds.id_project
|
|
||||||
SET messages.timezone = projects.timezone,
|
|
||||||
messages.led = messages.led;
|
|
||||||
|
|
||||||
UPDATE posts
|
|
||||||
SET timezone = 'Europe/Paris',
|
|
||||||
led = led;
|
|
||||||
|
|
||||||
UPDATE posts
|
|
||||||
INNER JOIN projects ON projects.id_project = posts.id_project
|
|
||||||
SET posts.id_user = 1,
|
|
||||||
posts.timezone = projects.timezone,
|
|
||||||
posts.led = posts.led
|
|
||||||
WHERE posts.name IN ('francois', 'françois','Francois', 'François', 'franzz');
|
|
||||||
|
|
||||||
UPDATE posts
|
|
||||||
SET timezone = 'Pacific/Auckland',
|
|
||||||
led = led
|
|
||||||
WHERE name = 'nz';
|
|
||||||
|
|
||||||
UPDATE posts
|
|
||||||
SET timezone = 'Atlantic/Madeira',
|
|
||||||
led = led
|
|
||||||
WHERE id_post IN (141, 142);
|
|
||||||
|
|
||||||
UPDATE medias
|
|
||||||
INNER JOIN projects ON projects.id_project = medias.id_project
|
|
||||||
SET medias.timezone = projects.timezone,
|
|
||||||
medias.led = medias.led;
|
|
||||||
|
|
||||||
UPDATE medias
|
|
||||||
SET timezone = 'Atlantic/Madeira',
|
|
||||||
taken_on = posted_on,
|
|
||||||
led = led
|
|
||||||
WHERE id_media IN (64, 65);
|
|
||||||
|
|
||||||
ALTER TABLE projects DROP COLUMN timezone;
|
|
||||||
|
|
||||||
UPDATE maps SET attribution = 'OpenTopoMap (CC-BY-SA)' WHERE id_map = 2;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE users ADD clearance TINYINT(1) DEFAULT 0 AFTER active;
|
|
||||||
UPDATE users SET clearance = 9 WHERE email = 'francois.lutran@gmail.com';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE messages ADD posted_on TIMESTAMP DEFAULT 0 AFTER battery_state;
|
|
||||||
UPDATE messages SET posted_on = led, led = led;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ALTER TABLE messages ADD weather_icon VARCHAR(30) AFTER posted_on;
|
|
||||||
ALTER TABLE messages ADD weather_cond VARCHAR(30) AFTER weather_icon;
|
|
||||||
ALTER TABLE messages ADD weather_temp DECIMAL(3,1) AFTER weather_cond;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
INSERT INTO maps (codename, geo_name, min_zoom, max_zoom, attribution) VALUES ('outdoors', 'mapbox.outdoors', 0, 19, '');
|
|
||||||
ALTER TABLE maps ADD COLUMN tile_size SMALLINT UNSIGNED DEFAULT 256 AFTER geo_name;
|
|
||||||
UPDATE maps SET tile_size = 512 WHERE geo_name = 'mapbox.outdoors';
|
|
||||||
UPDATE maps SET tile_size = 512 WHERE geo_name = 'mapbox.satellite-streets';
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
ALTER TABLE maps ADD pattern VARCHAR(200) NOT NULL AFTER geo_name;
|
|
||||||
UPDATE maps SET pattern = CONCAT('http://localhost/geo/?a=tile&id=', geo_name, '&z={z}&x={x}&y={y}') WHERE geo_name <> '';
|
|
||||||
|
|
||||||
ALTER TABLE maps ADD token VARCHAR(4096) AFTER pattern;
|
|
||||||
UPDATE maps SET token = '';
|
|
||||||
|
|
||||||
ALTER TABLE maps DROP geo_name;
|
|
||||||
|
|
||||||
INSERT INTO maps (codename, pattern, token, tile_size, min_zoom, max_zoom, attribution)
|
|
||||||
VALUES ('static', 'http://localhost/geo/?a=tile&id=static&z=13&x={x}&y={y}', '', 400, 13, 13, '');
|
|
||||||
|
|
||||||
INSERT INTO maps (codename, pattern, token, tile_size, min_zoom, max_zoom, attribution)
|
|
||||||
VALUES ('static_marker', 'http://localhost/geo/?a=tile&id=static.marker&z=13&x={x}&y={y}&marker=http://localhost/spot/images/footprint_mapbox.png', '', 400, 13, 13, '');
|
|
||||||
|
|
||||||
UPDATE maps SET max_zoom = 17 WHERE codename = 'otm';
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
ALTER TABLE medias ADD width INT AFTER timezone;
|
|
||||||
ALTER TABLE medias ADD height INT AFTER width;
|
|
||||||
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (1).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (10).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (11).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (12).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (13).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (14).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (15).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (16).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (17).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (18).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (19).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (2).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (20).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (21).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (22).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (23).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (24).jpg';
|
|
||||||
UPDATE medias SET width = 640, height = 1136 WHERE filename = 'image (25).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (26).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (27).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (28).jpg';
|
|
||||||
UPDATE medias SET width = 960, height = 1280 WHERE filename = 'image (29).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (3).jpg';
|
|
||||||
UPDATE medias SET width = 960, height = 1280 WHERE filename = 'image (30).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (31).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (32).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (33).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (34).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (35).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (36).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (37).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (38).jpg';
|
|
||||||
UPDATE medias SET width = 2048, height = 1365 WHERE filename = 'image (39).jpg';
|
|
||||||
UPDATE medias SET width = 1280, height = 960 WHERE filename = 'image (4).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (40).jpg';
|
|
||||||
UPDATE medias SET width = 8192, height = 1856 WHERE filename = 'image (41).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (42).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (43).jpg';
|
|
||||||
UPDATE medias SET width = 8192, height = 1856 WHERE filename = 'image (44).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (45).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (46).jpg';
|
|
||||||
UPDATE medias SET width = 640, height = 1038 WHERE filename = 'image (47).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (48).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (49).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (5).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (50).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (51).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (52).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (53).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (54).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (55).jpg';
|
|
||||||
UPDATE medias SET width = 3648, height = 5472 WHERE filename = 'image (56).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (57).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (58).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (59).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (6).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (60).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (61).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (62).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'image (63).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'image (7).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (8).jpg';
|
|
||||||
UPDATE medias SET width = 2448, height = 3264 WHERE filename = 'image (9).jpg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'DSC01187[1].JPG';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'DSC01477.JPG';
|
|
||||||
UPDATE medias SET width = 2726, height = 4089 WHERE filename = 'DSC03114.jpg';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'IMG_6011.MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = '156E315D-88AD-492C-BE97-9854FED48FF7.MOV';
|
|
||||||
UPDATE medias SET width = 2576, height = 1932 WHERE filename = '4D06AAF9-A244-4AEF-A1B0-A3C439673840.jpeg';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = '06361CBA-F514-4789-A498-47D681881DDF.MOV';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'B0A15DCC-B5BB-4BE3-ADBA-CBCB51C178F6.jpeg';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = 'F827F48B-5CC4-4DA7-A80B-FFED47F6EC60.MOV';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'EC407D2D-0ECF-46C7-BC05-73F48D152182.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '7961DA2D-288D-4B0B-B4A6-400673E23BA6.jpeg';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'B43B0160-DAB4-433A-9064-918C40744D27.MOV';
|
|
||||||
UPDATE medias SET width = 1536, height = 2134 WHERE filename = 'C0D5CCF6-FE72-424C-A040-96D8C34FBE9E.jpeg';
|
|
||||||
UPDATE medias SET width = 2154, height = 1850 WHERE filename = '0940EF97-4C27-4304-A663-654F0DE5FAB2.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '7597DEC0-1BD3-4B93-926B-E4D8A4B28E61.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '3A1F777D-58F6-40BA-8AAE-1EA236581BA3.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'C6AC86F9-C819-4866-AA71-A31375E4CCC4.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'B936E022-4D70-4C9E-9EAE-308FB6F91816.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'C777A1C7-7C3A-4ADC-8A5C-CEE8FB047B79.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'B76D8452-FCC4-4FCE-8686-2A4B8C7832FF.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '313F8490-E885-42F5-BB59-ACFB12250F0F.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'F0C1EFDB-0A4F-4FC0-A46D-A396D8724054.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'E9339EEF-A21C-48A6-B6CC-23C3A512DA29.jpeg';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = '5150FFEF-A715-4F5A-8562-1502BE778B6A.MOV';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'C8CD8B78-3EC9-4700-9A46-478556496239.jpeg';
|
|
||||||
UPDATE medias SET width = 1536, height = 2048 WHERE filename = '3D83F7D3-B746-440F-8DD3-6DADF230AC28.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'B9121A78-0B21-42E2-8392-A60BCE240EFE.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '5A3755D0-81CB-43B1-9750-AB34F413D526.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'B9EFBC4F-4FC4-4D03-9722-9A1FF61F3B8E.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'F7C6FFFF-BF61-42BF-949B-72CD2CAAEFE2.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'B0557309-579D-4C1A-863F-CF4898C42E77.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '2C75F8E9-C3E0-4FD5-AF2E-CE339545D6BC.jpeg';
|
|
||||||
UPDATE medias SET width = 3891, height = 2917 WHERE filename = 'C6E18BC2-B407-4F2C-82F9-87244A9AD311.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '0A4B5812-D5CD-4CB0-989B-C95D15526F1A.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '1548CCA0-C8C8-4DC6-BD96-ABAA4589E825.jpeg';
|
|
||||||
UPDATE medias SET width = 4685, height = 3116 WHERE filename = '7F80807F-D40F-4714-81AE-3CD7CD3FE8CD.jpeg';
|
|
||||||
UPDATE medias SET width = 5283, height = 3519 WHERE filename = '32F5B460-958D-4A13-92BD-371BBC0DA769.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'F99EEC87-C5CC-49AD-BCBF-8A14ADADD262.jpeg';
|
|
||||||
UPDATE medias SET width = 8192, height = 1856 WHERE filename = 'C243E61F-2A31-4179-97B4-AD54C02E88D5.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '85807A81-91CB-4B10-861B-1B3C5A60F066.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'DE99B389-738F-48CB-BEF1-D448DEB16E7E.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '6BBD7291-BDFC-460F-A620-9D2F68AF637F.jpeg';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = '7DB6662E-353A-4AC5-9331-D9122D956FE7.MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = '974841E5-AB13-4067-AD5F-3EDEFEAE28E4.MOV';
|
|
||||||
UPDATE medias SET width = 1536, height = 2304 WHERE filename = '09F007C7-53BA-4214-B3AF-DB0E0CDD1994.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '6B92D63D-EDA5-4787-8AC7-4A0F65DC071F.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'BAF3BB55-E012-40CD-8D8B-27C4398C1D00.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '8FA2F4F0-F220-4E98-9BC0-ACB607FC2F8E.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'C260B1AD-BA4C-4835-B132-A529A9319D4D.jpeg';
|
|
||||||
UPDATE medias SET width = 1536, height = 2304 WHERE filename = '92F8E35B-5FF8-414D-A627-46FF45E6CCCA.jpeg';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = '33AA17D3-CFD6-426A-B2D5-CD639F53C081.MOV';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '0015713D-18A2-45A7-B26F-3954AC0979D3.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '223A23C5-71BB-4419-8765-CE51F3E69480.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '9ED663DD-0257-4211-A993-C86CDAC9066B.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '1ECF5050-8821-441C-8DF7-E93B24941F8A.jpeg';
|
|
||||||
UPDATE medias SET width = 1536, height = 2304 WHERE filename = '68211D98-D6E7-4D7C-BB82-7E5E30884C7D.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '61747CFA-859E-4859-852F-3AE2650C7578.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'F17CD761-DFDC-4E5F-97A7-C16035A8D8EB.jpeg';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = '7EE0B25A-0BC6-4BF1-9CDF-9E4547CFEAF4.MOV';
|
|
||||||
UPDATE medias SET width = 3503, height = 2625 WHERE filename = '273092DD-F6CE-4C74-8878-A4D94A9C78F9.jpeg';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = '4853B2CB-5BC4-45FE-AD02-0980953DB59A.MOV';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '789210A7-293D-4119-87EE-1B7CA0ACBE0A.jpeg';
|
|
||||||
UPDATE medias SET width = 2665, height = 1996 WHERE filename = '52D57195-22A2-423B-AA35-05D48B889950.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'F5644C0F-5244-4574-BBE1-4059794F87E1.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = '7BB3D57A-D5BE-45D5-8CAC-C3172A89C67A.jpeg';
|
|
||||||
UPDATE medias SET width = 2687, height = 2013 WHERE filename = '8345C480-1274-45DB-A990-40913DD219A7.jpeg';
|
|
||||||
UPDATE medias SET width = 4128, height = 3096 WHERE filename = '28567B3E-A067-41AE-AE69-37DD3C98F820.jpeg';
|
|
||||||
UPDATE medias SET width = 3096, height = 4128 WHERE filename = '20190827_133201_DxO.jpg';
|
|
||||||
UPDATE medias SET width = 3096, height = 4128 WHERE filename = '20190827_133201_DxO (1).jpg';
|
|
||||||
UPDATE medias SET width = 3096, height = 4128 WHERE filename = '20190827_133201_DxO (2).jpg';
|
|
||||||
UPDATE medias SET width = 3096, height = 4128 WHERE filename = '20190827_133201_DxO (3).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 3000 WHERE filename = 'Untitled.png';
|
|
||||||
UPDATE medias SET width = 2000, height = 3000 WHERE filename = 'Untitled (1).png';
|
|
||||||
UPDATE medias SET width = 2000, height = 3000 WHERE filename = 'Untitled (2).png';
|
|
||||||
UPDATE medias SET width = 2000, height = 3000 WHERE filename = 'Untitled (3).png';
|
|
||||||
UPDATE medias SET width = 2000, height = 3000 WHERE filename = 'Untitled (4).png';
|
|
||||||
UPDATE medias SET width = 2000, height = 3000 WHERE filename = 'Untitled (5).png';
|
|
||||||
UPDATE medias SET width = 2000, height = 3000 WHERE filename = 'Untitled (6).png';
|
|
||||||
UPDATE medias SET width = 2000, height = 3000 WHERE filename = 'Untitled (7).png';
|
|
||||||
UPDATE medias SET width = 2000, height = 3000 WHERE filename = 'Untitled (8).png';
|
|
||||||
UPDATE medias SET width = 365, height = 600 WHERE filename = 'asunabg.png';
|
|
||||||
UPDATE medias SET width = 365, height = 600 WHERE filename = 'asunabg (1).png';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'iPhone 6S (1).jpeg';
|
|
||||||
UPDATE medias SET width = 3024, height = 4032 WHERE filename = 'IMG_2591.JPG';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'CF0432E7-5221-433D-A681-DBABE131EE99.jpeg';
|
|
||||||
UPDATE medias SET width = 694, height = 628 WHERE filename = 'Capture.PNG';
|
|
||||||
UPDATE medias SET width = 3024, height = 4032 WHERE filename = 'IMG_2232 (1).jpg';
|
|
||||||
UPDATE medias SET width = 1350, height = 900 WHERE filename = 'Photo 2014-06-21 20-17-11 (_MG_1217).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945) (9).jpg';
|
|
||||||
UPDATE medias SET width = 1154, height = 770 WHERE filename = 'Photo 2014-06-21 20-17-11 (_MG_1217)_DxO (3).jpg';
|
|
||||||
UPDATE medias SET width = 3024, height = 4032 WHERE filename = 'IMG_2232 (2).jpg';
|
|
||||||
UPDATE medias SET width = 2320, height = 3088 WHERE filename = '9C0965E1-6A40-4691-8127-35B2F1D6BB58.jpeg';
|
|
||||||
UPDATE medias SET width = 3024, height = 4032 WHERE filename = 'IMG_3077.jpg';
|
|
||||||
UPDATE medias SET width = 3024, height = 4032 WHERE filename = 'AF7A1DBC-E1CC-4FEE-8411-AC25BF69C0B5.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'F7FC1246-6304-4662-A5F2-230A29728EBC.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'CA5BA9A6-2A53-430A-A0BF-B8D0C0CDC983.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '12E87114-6B4B-4051-945C-9042FBBF5E3E.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'F97B5058-C7D7-4B25-B727-914A8897CE58.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'D1E626E6-6F49-4F62-A3D5-B19CBC906F7B.jpeg';
|
|
||||||
UPDATE medias SET width = 3024, height = 4032 WHERE filename = 'IMG_1228_DxO.jpg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'IMG_1268_DxO.jpg';
|
|
||||||
UPDATE medias SET width = 3024, height = 4032 WHERE filename = 'IMG_2232.jpg';
|
|
||||||
UPDATE medias SET width = 2475, height = 2475 WHERE filename = 'IMG_2232_DxO (1).jpg';
|
|
||||||
UPDATE medias SET width = 2475, height = 2475 WHERE filename = 'IMG_2232_DxO.jpg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'IMG_2317.jpg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'IMG_2574 (1).jpg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'IMG_2574 (2).jpg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'IMG_2574.jpg';
|
|
||||||
UPDATE medias SET width = 1344, height = 672 WHERE filename = 'PHOTO-2019-12-24-23-40-26.jpg';
|
|
||||||
UPDATE medias SET width = 1512, height = 2688 WHERE filename = 'PXL_20210405_083835976.jpg';
|
|
||||||
UPDATE medias SET width = 3840, height = 2160 WHERE filename = 'PXL_20210405_091516836.jpg';
|
|
||||||
UPDATE medias SET width = 1350, height = 900 WHERE filename = 'Photo 2014-06-21 20-17-11 (_MG_1217)_DxO (1).jpg';
|
|
||||||
UPDATE medias SET width = 1350, height = 900 WHERE filename = 'Photo 2014-06-21 20-17-11 (_MG_1217)_DxO (2).jpg';
|
|
||||||
UPDATE medias SET width = 1350, height = 900 WHERE filename = 'Photo 2014-06-21 20-17-11 (_MG_1217)_DxO.jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945) (1).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945) (2).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945) (3).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945) (4).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945) (5).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945) (6).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945) (7).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945) (8).jpg';
|
|
||||||
UPDATE medias SET width = 2000, height = 1500 WHERE filename = 'Photo 2015-01-11 20-30-03 (P1020945).jpg';
|
|
||||||
UPDATE medias SET width = 1500, height = 2000 WHERE filename = 'Photo 2015-01-19 19-59-34 (P1040055).jpg';
|
|
||||||
UPDATE medias SET width = 3264, height = 2448 WHERE filename = 'TEST.jpg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'image (64).jpg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'image (65).jpg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'image.jpg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '083932A9-4248-4208-91CC-34DCDC374E7A.jpeg';
|
|
||||||
UPDATE medias SET width = 3024, height = 4032 WHERE filename = '102ED557-69B0-41F2-9193-DF52FEEFD35C.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '45289F93-5D82-40DE-A0B1-1871317C56C1.jpeg';
|
|
||||||
UPDATE medias SET width = 1200, height = 1600 WHERE filename = '5F97CB55-46C7-4BB7-9335-BBDFF77F1310.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '634716D8-D7E7-4128-B0D2-EEB4437411F3.jpeg';
|
|
||||||
UPDATE medias SET width = 3024, height = 4032 WHERE filename = '6C111386-591F-450D-9794-E812FB6FF036.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '6D718B33-020D-460A-9194-D637A2590ABC.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '866196A9-5428-4E6C-8C4A-03DB284A9448.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'A68E6383-095C-4CB2-876B-4EC59DB24419.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'APPLE (1).jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'APPLE.jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'B9EFBC4F-4FC4-4D03-9722-9A1FF61F3B8E (1).jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'B9EFBC4F-4FC4-4D03-9722-9A1FF61F3B8E (2).jpeg';
|
|
||||||
UPDATE medias SET width = 5472, height = 3648 WHERE filename = 'B9EFBC4F-4FC4-4D03-9722-9A1FF61F3B8E (3).jpeg';
|
|
||||||
UPDATE medias SET width = 1152, height = 1536 WHERE filename = 'BA37E256-C0B2-4DE6-B0E0-659EB2C3411B.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'D4009C41-7B4C-42D5-9FB4-9CEC7CC1B4B0.jpeg';
|
|
||||||
UPDATE medias SET width = 1200, height = 1600 WHERE filename = 'DDFADE5F-2785-4168-9EAB-D63818566929.jpeg';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = 'iPhone 6S.jpeg';
|
|
||||||
UPDATE medias SET width = 365, height = 600 WHERE filename = 'asunabg (2).png';
|
|
||||||
UPDATE medias SET width = 365, height = 600 WHERE filename = 'asunabg (3).png';
|
|
||||||
UPDATE medias SET width = 365, height = 600 WHERE filename = 'asunabg (4).png';
|
|
||||||
UPDATE medias SET width = 200, height = 120 WHERE filename = 'ffprobeall.png';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = 'temp_60a6954ea732b.png';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = 'temp_60a695501d507.png';
|
|
||||||
UPDATE medias SET width = 1080, height = 1920 WHERE filename = 'temp_60a695509009d.png';
|
|
||||||
UPDATE medias SET width = 1080, height = 1920 WHERE filename = 'temp_60a695520a9a0.png';
|
|
||||||
UPDATE medias SET width = 1080, height = 1920 WHERE filename = 'temp_60a695535d689.png';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = '7DB6662E-353A-4AC5-9331-D9122D956FE7 (1).MOV';
|
|
||||||
UPDATE medias SET width = 1280, height = 720 WHERE filename = '7DB6662E-353A-4AC5-9331-D9122D956FE7 (2).MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'IMG_2584 (1).MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'IMG_2584.MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'IMG_2585 (1).MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'IMG_2585 (2).MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'IMG_2585 (3).MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'IMG_2585 (4).MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'IMG_2585 (5).MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'IMG_2585.MOV';
|
|
||||||
UPDATE medias SET width = 1920, height = 1080 WHERE filename = 'iPhone 6S.MOV';
|
|
||||||
UPDATE medias SET width = 4032, height = 3024 WHERE filename = '1620E5B9-C65D-4252-A495-18D5CAD63C6E.jpeg';
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
ALTER TABLE messages MODIFY ref_msg_id VARCHAR(15) NOT NULL;
|
|
||||||
ALTER TABLE messages ADD display BOOLEAN DEFAULT 1 AFTER weather_temp;
|
|
||||||
|
|
||||||
UPDATE messages SET display = 0 WHERE id_message = 197;
|
|
||||||
UPDATE messages SET display = 0 WHERE id_message = 216;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
CREATE TABLE `projects` (
|
|
||||||
`id_project` int(10) UNSIGNED auto_increment,
|
|
||||||
`codename` VARCHAR(100),
|
|
||||||
`name` VARCHAR(100),
|
|
||||||
`active_from` DATETIME,
|
|
||||||
`active_to` DATETIME,
|
|
||||||
`geofile` VARCHAR(50),
|
|
||||||
`timezone` VARCHAR(100),
|
|
||||||
`led` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id_project`));
|
|
||||||
|
|
||||||
INSERT INTO projects (name, codename, active_from, active_to, geofile, timezone) VALUES ('Te Araroa', 'te_araroa', '2015-12-29 00:00:00', '2016-03-05 23:59:59', 'te_araroa.geojson', 'Pacific/Auckland');
|
|
||||||
INSERT INTO projects (name, codename, active_from, active_to, geofile, timezone) VALUES ('HRP', 'hrp', '2019-06-01 00:00:00', '2019-09-10 23:59:59', 'hrp.geojson', 'Europe/Paris');
|
|
||||||
|
|
||||||
ALTER TABLE feeds ADD COLUMN id_project int(10) UNSIGNED AFTER id_spot;
|
|
||||||
ALTER TABLE feeds ADD last_update DATETIME AFTER status;
|
|
||||||
ALTER TABLE feeds ADD FOREIGN KEY (`id_project`) REFERENCES projects(`id_project`);
|
|
||||||
UPDATE feeds SET last_update = led;
|
|
||||||
UPDATE feeds SET id_project = 1;
|
|
||||||
|
|
||||||
ALTER TABLE posts ADD COLUMN id_project int(10) UNSIGNED AFTER id_post;
|
|
||||||
ALTER TABLE posts ADD timestamp DATETIME AFTER content;
|
|
||||||
ALTER TABLE posts ADD FOREIGN KEY (`id_project`) REFERENCES projects(`id_project`);
|
|
||||||
UPDATE posts SET timestamp = led;
|
|
||||||
UPDATE posts SET id_project = 1;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ALTER TABLE medias ADD latitude DECIMAL(7,5) AFTER timezone;
|
|
||||||
ALTER TABLE medias ADD longitude DECIMAL(8,5) AFTER latitude;
|
|
||||||
ALTER TABLE medias ADD altitude SMALLINT AFTER longitude;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
ALTER TABLE mappings ADD COLUMN default_map BOOLEAN DEFAULT 0 AFTER id_project;
|
|
||||||
ALTER TABLE mappings ADD CONSTRAINT default_on_generic_map_only CHECK (default_map = 0 OR id_project IS NULL);
|
|
||||||
UPDATE mappings SET default_map = 1 WHERE id_map = (select id_map from maps where codename = 'satellite');
|
|
||||||
UPDATE maps SET token = substring(pattern, locate('token=', pattern) + 6) WHERE codename = 'static_marker';
|
|
||||||
UPDATE maps SET pattern = replace(pattern, token, '{token}') WHERE codename = 'static_marker';
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
ALTER TABLE medias MODIFY latitude DECIMAL(8,6);
|
|
||||||
ALTER TABLE medias MODIFY longitude DECIMAL(9,6);
|
|
||||||
|
|
||||||
UPDATE medias SET comment = 'Source chaude en plein milieu d''une forêt !' WHERE id_media = 16;
|
|
||||||
UPDATE medias SET comment = 'Stephan veut absolument arriver à Arrowtown avant le Super Bowl :D' WHERE id_media = 48;
|
|
||||||
|
|
||||||
UPDATE medias SET latitude = 41.011880, longitude = -121.652212, altitude = 855 WHERE id_media = 286;
|
|
||||||
UPDATE medias SET latitude = -41.787646,longitude = 172.886950 WHERE id_media = 62;
|
|
||||||
UPDATE medias SET latitude = -43.575937,longitude = 170.945159, comment = 'Edoras' WHERE id_media = 17;
|
|
||||||
UPDATE medias SET latitude = -44.176388,longitude = 170.196363, comment = 'Juste la bonne ouverture !' WHERE id_media = 29;
|
|
||||||
UPDATE medias SET latitude = -43.695997,longitude = 170.168364, comment = 'Tasman Glacier' WHERE id_media = 31;
|
|
||||||
UPDATE medias SET latitude = -44.802931,longitude = 168.157397 WHERE id_media = 52;
|
|
||||||
UPDATE medias SET latitude = 42.475460, longitude = 3.040459 WHERE id_media = 70;
|
|
||||||
UPDATE medias SET latitude = 42.701617, longitude = 0.526217 WHERE id_media = 104;
|
|
||||||
UPDATE medias SET latitude = 42.715667, longitude = 0.028215 WHERE id_media = 107;
|
|
||||||
UPDATE medias SET latitude = 42.691005, longitude = -0.033730 WHERE id_media = 114;
|
|
||||||
UPDATE medias SET latitude = 42.789225, longitude = -0.155126 WHERE id_media = 122;
|
|
||||||
UPDATE medias SET latitude = 43.085268, longitude = -1.389657 WHERE id_media = 127;
|
|
||||||
UPDATE medias SET latitude = 43.307165, longitude = -1.630111 WHERE id_media = 129;
|
|
||||||
UPDATE medias SET latitude = 57.278450, longitude = -5.289557 WHERE id_media = 396;
|
|
||||||
UPDATE medias SET latitude = 58.208948, longitude = -4.927144, comment = 'Eas a'' Chual Aluinn (nom de la chute d’eau, c’est du gaélique, faut pas chercher). Les plus hautes des UK.' WHERE id_media = 409;
|
|
||||||
UPDATE medias SET latitude = 58.243837, longitude = -4.964172 WHERE id_media = 410;
|
|
||||||
UPDATE medias SET latitude = 58.542221, longitude = -5.048463 WHERE id_media = 417;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
CREATE TABLE `pictures` (
|
|
||||||
`id_picture` int(10) UNSIGNED auto_increment,
|
|
||||||
`id_project` int(10) UNSIGNED,
|
|
||||||
`filename` VARCHAR(100) NOT NULL,
|
|
||||||
`taken_on` DATETIME,
|
|
||||||
`timestamp` DATETIME,
|
|
||||||
`rotate` SMALLINT,
|
|
||||||
`led` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id_picture`),
|
|
||||||
UNIQUE KEY `uni_file_name` (`filename`)
|
|
||||||
);
|
|
||||||
|
|
||||||
ALTER TABLE pictures ADD INDEX(`id_project`);
|
|
||||||
ALTER TABLE pictures ADD FOREIGN KEY (`id_project`) REFERENCES projects(`id_project`);
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
ALTER TABLE messages ADD iso_time VARCHAR(24) AFTER longitude;
|
|
||||||
ALTER TABLE messages CHANGE COLUMN timestamp site_time TIMESTAMP DEFAULT 0;
|
|
||||||
ALTER TABLE messages CHANGE COLUMN unix_timestamp unix_time INT;
|
|
||||||
UPDATE messages SET iso_time = CONCAT(REPLACE(CONVERT_TZ(FROM_UNIXTIME(unix_time), @@session.time_zone, 'Pacific/Auckland'), ' ', 'T'), '+1200') WHERE id_feed = 1;
|
|
||||||
|
|
||||||
ALTER TABLE feeds MODIFY last_update TIMESTAMP DEFAULT 0;
|
|
||||||
|
|
||||||
ALTER TABLE projects MODIFY active_from TIMESTAMP DEFAULT 0;
|
|
||||||
ALTER TABLE projects MODIFY active_to TIMESTAMP DEFAULT 0;
|
|
||||||
|
|
||||||
ALTER TABLE posts CHANGE COLUMN timestamp site_time TIMESTAMP DEFAULT 0;
|
|
||||||
|
|
||||||
ALTER TABLE pictures CHANGE COLUMN timestamp posted_on TIMESTAMP DEFAULT 0;
|
|
||||||
UPDATE pictures INNER JOIN projects USING(id_project) SET taken_on = CONVERT_TZ(taken_on, projects.timezone, @@session.time_zone) where id_project = 1;
|
|
||||||
ALTER TABLE pictures MODIFY taken_on TIMESTAMP DEFAULT 0;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
UPDATE projects SET geofile = REPLACE(geofile, '.geojson', '');
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
RENAME TABLE pictures TO medias;
|
|
||||||
ALTER TABLE medias CHANGE COLUMN id_picture id_media INT(10) UNSIGNED NOT NULL auto_increment;
|
|
||||||
ALTER TABLE medias ADD COLUMN type VARCHAR(20) AFTER filename;
|
|
||||||
UPDATE medias SET type = 'image';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
/* Remove NO_ZERO_DATE mode, checks mode with: SELECT @@SQL_MODE, @@GLOBAL.SQL_MODE; and: SET @@SQL_MODE = REPLACE(@@SQL_MODE, 'NO_ZERO_DATE', ''); */
|
|
||||||
ALTER TABLE medias ADD comment LONGTEXT AFTER rotate;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE projects DROP COLUMN geofile;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
CREATE TABLE `users` (
|
|
||||||
`id_user` int(10) UNSIGNED auto_increment,
|
|
||||||
`name` VARCHAR(100),
|
|
||||||
`email` VARCHAR(320),
|
|
||||||
`language` VARCHAR(2),
|
|
||||||
`active` BOOLEAN,
|
|
||||||
`led` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id_user`),
|
|
||||||
UNIQUE KEY `uni_email` (`email`)
|
|
||||||
);
|
|
||||||
|
|
||||||
ALTER TABLE posts ADD COLUMN id_user int(10) UNSIGNED AFTER id_project;
|
|
||||||
ALTER TABLE posts ADD INDEX(`id_user`);
|
|
||||||
ALTER TABLE posts ADD FOREIGN KEY (`id_user`) REFERENCES users(`id_user`);
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
ALTER TABLE users ADD COLUMN timezone char(64) AFTER language;
|
|
||||||
ALTER TABLE users MODIFY COLUMN email VARCHAR(320) NOT NULL;
|
|
||||||
UPDATE users SET timezone = 'Europe/Paris';
|
|
||||||
|
|
||||||
ALTER TABLE projects MODIFY COLUMN timezone char(64);
|
|
||||||
0
config/settings-sample.php
Executable file → Normal file
265
gaia/tracks.js
782
gaia/upload.js
@@ -1,782 +0,0 @@
|
|||||||
// ==UserScript==
|
|
||||||
// @name GaiaGps Uploader
|
|
||||||
// @namespace https://greasyfork.org/users/583371
|
|
||||||
// @description Allow the user to upload multiple files at once and more than 1000 waypoints
|
|
||||||
// @grant none
|
|
||||||
// @version 3.1.2
|
|
||||||
// @author Franzz
|
|
||||||
// @license GNU GPLv3
|
|
||||||
// @match https://www.gaiagps.com/map/*
|
|
||||||
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
|
|
||||||
// ==/UserScript==
|
|
||||||
|
|
||||||
/* jshint esversion: 6 */
|
|
||||||
|
|
||||||
//Ctrl+Alt+Shift+I to open network tab on addon
|
|
||||||
|
|
||||||
/* GPXParser - v3.0.8 - https://github.com/Luuka/GPXParser.js/blob/master/src/GPXParser.js */
|
|
||||||
/* Personnal modifications have been applied, care on upgrade */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GPX file parser
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
let gpxParser = function () {
|
|
||||||
this.xmlSource = "";
|
|
||||||
this.metadata = {};
|
|
||||||
this.waypoints = [];
|
|
||||||
this.tracks = [];
|
|
||||||
this.routes = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a gpx formatted string to a GPXParser Object
|
|
||||||
*
|
|
||||||
* @param {string} gpxstring - A GPX formatted String
|
|
||||||
*
|
|
||||||
* @return {gpxParser} A GPXParser object
|
|
||||||
*/
|
|
||||||
gpxParser.prototype.parse = function (gpxstring) {
|
|
||||||
let keepThis = this;
|
|
||||||
|
|
||||||
let domParser = new window.DOMParser();
|
|
||||||
this.xmlSource = domParser.parseFromString(gpxstring, 'text/xml');
|
|
||||||
|
|
||||||
let metadata = this.xmlSource.querySelector('metadata');
|
|
||||||
if(metadata != null){
|
|
||||||
this.metadata.name = this.getElementValue(metadata, "name");
|
|
||||||
this.metadata.desc = this.getElementValue(metadata, "desc");
|
|
||||||
this.metadata.time = this.getElementValue(metadata, "time");
|
|
||||||
|
|
||||||
let author = {};
|
|
||||||
let authorElem = metadata.querySelector('author');
|
|
||||||
if(authorElem != null){
|
|
||||||
author.name = this.getElementValue(authorElem, "name");
|
|
||||||
author.email = {};
|
|
||||||
let emailElem = authorElem.querySelector('email');
|
|
||||||
if(emailElem != null){
|
|
||||||
author.email.id = emailElem.getAttribute("id");
|
|
||||||
author.email.domain = emailElem.getAttribute("domain");
|
|
||||||
}
|
|
||||||
|
|
||||||
let link = {};
|
|
||||||
let linkElem = authorElem.querySelector('link');
|
|
||||||
if(linkElem != null){
|
|
||||||
link.href = linkElem.getAttribute('href');
|
|
||||||
link.text = this.getElementValue(linkElem, "text");
|
|
||||||
link.type = this.getElementValue(linkElem, "type");
|
|
||||||
}
|
|
||||||
author.link = link;
|
|
||||||
}
|
|
||||||
this.metadata.author = author;
|
|
||||||
|
|
||||||
let link = {};
|
|
||||||
let linkElem = this.queryDirectSelector(metadata, 'link');
|
|
||||||
if(linkElem != null){
|
|
||||||
link.href = linkElem.getAttribute('href');
|
|
||||||
link.text = this.getElementValue(linkElem, "text");
|
|
||||||
link.type = this.getElementValue(linkElem, "type");
|
|
||||||
this.metadata.link = link;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var wpts = [].slice.call(this.xmlSource.querySelectorAll('wpt'));
|
|
||||||
for (let idx in wpts){
|
|
||||||
var wpt = wpts[idx];
|
|
||||||
let pt = {};
|
|
||||||
pt.name = keepThis.getElementValue(wpt, "name");
|
|
||||||
pt.lat = parseFloat(wpt.getAttribute("lat"));
|
|
||||||
pt.lon = parseFloat(wpt.getAttribute("lon"));
|
|
||||||
//let floatValue = parseFloat(keepThis.getElementValue(wpt, "ele"));
|
|
||||||
//pt.ele = isNaN(floatValue) ? null : floatValue;
|
|
||||||
pt.ele = parseFloat(keepThis.getElementValue(wpt, "ele")) || null;
|
|
||||||
pt.cmt = keepThis.getElementValue(wpt, "cmt");
|
|
||||||
pt.desc = keepThis.getElementValue(wpt, "desc");
|
|
||||||
pt.sym = keepThis.getElementValue(wpt, "sym");
|
|
||||||
|
|
||||||
//let time = keepThis.getElementValue(wpt, "time");
|
|
||||||
//pt.time = time == null ? null : new Date(time);
|
|
||||||
pt.time = (keepThis.getElementValue(wpt, "time") || keepThis.metadata.time) || null;
|
|
||||||
|
|
||||||
keepThis.waypoints.push(pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
var rtes = [].slice.call(this.xmlSource.querySelectorAll('rte'));
|
|
||||||
for (let idx in rtes){
|
|
||||||
let rte = rtes[idx];
|
|
||||||
let route = {};
|
|
||||||
route.name = keepThis.getElementValue(rte, "name");
|
|
||||||
route.cmt = keepThis.getElementValue(rte, "cmt");
|
|
||||||
route.desc = keepThis.getElementValue(rte, "desc");
|
|
||||||
route.src = keepThis.getElementValue(rte, "src");
|
|
||||||
route.number= keepThis.getElementValue(rte, "number");
|
|
||||||
|
|
||||||
let type = keepThis.queryDirectSelector(rte, "type");
|
|
||||||
route.type = type != null ? type.innerHTML : null;
|
|
||||||
|
|
||||||
let link = {};
|
|
||||||
let linkElem= rte.querySelector('link');
|
|
||||||
if(linkElem != null){
|
|
||||||
link.href = linkElem.getAttribute('href');
|
|
||||||
link.text = keepThis.getElementValue(linkElem, "text");
|
|
||||||
link.type = keepThis.getElementValue(linkElem, "type");
|
|
||||||
}
|
|
||||||
route.link = link;
|
|
||||||
|
|
||||||
let routepoints = [];
|
|
||||||
var rtepts = [].slice.call(rte.querySelectorAll('rtept'));
|
|
||||||
|
|
||||||
for (let idxIn in rtepts){
|
|
||||||
let rtept = rtepts[idxIn];
|
|
||||||
let pt = {};
|
|
||||||
pt.lat = parseFloat(rtept.getAttribute("lat"));
|
|
||||||
pt.lon = parseFloat(rtept.getAttribute("lon"));
|
|
||||||
//let floatValue = parseFloat(keepThis.getElementValue(rtept, "ele"));
|
|
||||||
//pt.ele = isNaN(floatValue) ? null : floatValue;
|
|
||||||
pt.ele = parseFloat(keepThis.getElementValue(rtept, "ele")) || null;
|
|
||||||
|
|
||||||
//let time = keepThis.getElementValue(rtept, "time");
|
|
||||||
//pt.time = time == null ? null : new Date(time);
|
|
||||||
pt.time = (keepThis.getElementValue(rtept, "time") || keepThis.metadata.time) || null;
|
|
||||||
|
|
||||||
routepoints.push(pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
//route.distance = keepThis.calculDistance(routepoints);
|
|
||||||
//route.elevation = keepThis.calcElevation(routepoints);
|
|
||||||
//route.slopes = keepThis.calculSlope(routepoints, route.distance.cumul);
|
|
||||||
route.points = routepoints;
|
|
||||||
|
|
||||||
keepThis.routes.push(route);
|
|
||||||
}
|
|
||||||
|
|
||||||
var trks = [].slice.call(this.xmlSource.querySelectorAll('trk'));
|
|
||||||
for (let idx in trks){
|
|
||||||
let trk = trks[idx];
|
|
||||||
let track = {};
|
|
||||||
|
|
||||||
track.name = keepThis.getElementValue(trk, "name");
|
|
||||||
track.cmt = keepThis.getElementValue(trk, "cmt");
|
|
||||||
track.desc = keepThis.getElementValue(trk, "desc");
|
|
||||||
track.src = keepThis.getElementValue(trk, "src");
|
|
||||||
track.number= keepThis.getElementValue(trk, "number");
|
|
||||||
track.color = keepThis.getElementValue(trk, "DisplayColor");
|
|
||||||
track.time = keepThis.metadata.time;
|
|
||||||
|
|
||||||
let type = keepThis.queryDirectSelector(trk, "type");
|
|
||||||
track.type = type != null ? type.innerHTML : null;
|
|
||||||
|
|
||||||
let link = {};
|
|
||||||
let linkElem= trk.querySelector('link');
|
|
||||||
if(linkElem != null){
|
|
||||||
link.href = linkElem.getAttribute('href');
|
|
||||||
link.text = keepThis.getElementValue(linkElem, "text");
|
|
||||||
link.type = keepThis.getElementValue(linkElem, "type");
|
|
||||||
}
|
|
||||||
track.link = link;
|
|
||||||
|
|
||||||
let trackpoints = [];
|
|
||||||
let trkpts = [].slice.call(trk.querySelectorAll('trkpt'));
|
|
||||||
for (let idxIn in trkpts){
|
|
||||||
var trkpt = trkpts[idxIn];
|
|
||||||
let pt = {};
|
|
||||||
pt.lat = parseFloat(trkpt.getAttribute("lat"));
|
|
||||||
pt.lon = parseFloat(trkpt.getAttribute("lon"));
|
|
||||||
//let floatValue = parseFloat(keepThis.getElementValue(trkpt, "ele"));
|
|
||||||
//pt.ele = isNaN(floatValue) ? null : floatValue;
|
|
||||||
pt.ele = parseFloat(keepThis.getElementValue(trkpt, "ele")) || null;
|
|
||||||
|
|
||||||
//let time = keepThis.getElementValue(trkpt, "time");
|
|
||||||
//pt.time = time == null ? null : new Date(time);
|
|
||||||
pt.time = (keepThis.getElementValue(trkpt, "time") || keepThis.metadata.time) || null;
|
|
||||||
|
|
||||||
trackpoints.push(pt);
|
|
||||||
}
|
|
||||||
//track.distance = keepThis.calculDistance(trackpoints);
|
|
||||||
//track.elevation = keepThis.calcElevation(trackpoints);
|
|
||||||
//track.slopes = keepThis.calculSlope(trackpoints, track.distance.cumul);
|
|
||||||
track.points = trackpoints;
|
|
||||||
|
|
||||||
keepThis.tracks.push(track);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get value from a XML DOM element
|
|
||||||
*
|
|
||||||
* @param {Element} parent - Parent DOM Element
|
|
||||||
* @param {string} needle - Name of the searched element
|
|
||||||
*
|
|
||||||
* @return {} The element value
|
|
||||||
*/
|
|
||||||
gpxParser.prototype.getElementValue = function(parent, needle){
|
|
||||||
let elem = parent.querySelector(needle);
|
|
||||||
if(elem != null){
|
|
||||||
//Get value (in case of CDATA)
|
|
||||||
let sValue = (elem.innerHTML != undefined && elem.innerHTML.substring(0, 8) != '<![CDATA') ? elem.innerHTML : elem.childNodes[0].data;
|
|
||||||
|
|
||||||
//If decoded HTML, re-encode
|
|
||||||
if(sValue.substr(0, 4)== '<') sValue = $('<div>').html(sValue).text();
|
|
||||||
|
|
||||||
//Strip HTML tags & trim value
|
|
||||||
return $('<div>').html(sValue).text().trim();
|
|
||||||
}
|
|
||||||
return elem;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search the value of a direct child XML DOM element
|
|
||||||
*
|
|
||||||
* @param {Element} parent - Parent DOM Element
|
|
||||||
* @param {string} needle - Name of the searched element
|
|
||||||
*
|
|
||||||
* @return {} The element value
|
|
||||||
*/
|
|
||||||
gpxParser.prototype.queryDirectSelector = function(parent, needle) {
|
|
||||||
|
|
||||||
let elements = parent.querySelectorAll(needle);
|
|
||||||
let finalElem = elements[0];
|
|
||||||
|
|
||||||
if(elements.length > 1) {
|
|
||||||
let directChilds = parent.childNodes;
|
|
||||||
|
|
||||||
for(idx in directChilds) {
|
|
||||||
elem = directChilds[idx];
|
|
||||||
if(elem.tagName === needle) {
|
|
||||||
finalElem = elem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalElem;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Gaia {
|
|
||||||
static get URL() { return 'https://www.gaiagps.com'; }
|
|
||||||
static get API() { return Gaia.URL+'/api/objects'; }
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
this.asFiles = [];
|
|
||||||
this.aoWaypoints = [];
|
|
||||||
this.aoTracks = [];
|
|
||||||
this.asFolders = {};
|
|
||||||
this.progress = {current:0, total:0};
|
|
||||||
}
|
|
||||||
|
|
||||||
setLayout() {
|
|
||||||
this.reset();
|
|
||||||
|
|
||||||
/* FIXME: adapts on GaiaGPS upgrade */
|
|
||||||
let $InputButton = $('a[href="https://help.gaiagps.com/hc/en-us/articles/360052763513"]').parent().find('button');
|
|
||||||
|
|
||||||
//If the button is found (=displayed)
|
|
||||||
if($InputButton.length > 0) {
|
|
||||||
this.$InputBox = $InputButton.parent();
|
|
||||||
|
|
||||||
//Add Feedback box
|
|
||||||
this.$Feedback = ($('#ggu-feedback').length > 0)?$('#ggu-feedback'):($('<div>', {
|
|
||||||
'id':'ggu-feedback',
|
|
||||||
'style':'width:calc(100% + 64px); height:400px; margin:1em 0 0 -32px; display:inline-block; text-align:left; overflow:auto;'
|
|
||||||
}).insertAfter($InputButton));
|
|
||||||
|
|
||||||
/*
|
|
||||||
//Add Folder Name Input next to button
|
|
||||||
this.$InputName = ($('#ggu-inputname').length > 0)?$('#ggu-inputname'):($('<input>', {
|
|
||||||
'type':'text',
|
|
||||||
'id': 'ggu-inputname',
|
|
||||||
'name':'ggu-inputname',
|
|
||||||
'placeholder':'Folder Name',
|
|
||||||
'style': 'border-width:2px; border-color:rgba(0, 0, 0, 0.1); border-radius:4px; font-family:Inter,Helvetica Neue,Helvetica,Arial; font-size:15px; padding:8px 12px; margin-bottom:12px;'
|
|
||||||
}).val('PCT').prependTo(this.$InputBox));
|
|
||||||
*/
|
|
||||||
|
|
||||||
//Reset File Input DOM Element
|
|
||||||
let $InputFile = $('input[type=file]');
|
|
||||||
this.$InputFile = $InputFile.clone()
|
|
||||||
.insertAfter($InputFile)
|
|
||||||
.attr('multiple', 'multiple')
|
|
||||||
.attr('name', 'files[]')
|
|
||||||
.change(() => { this.readInputFiles(); });
|
|
||||||
$InputFile.remove();
|
|
||||||
|
|
||||||
//Reset button
|
|
||||||
this.$InputButton = $InputButton.clone()
|
|
||||||
.insertAfter($InputButton)
|
|
||||||
.click(() => {this.$InputFile.click();});
|
|
||||||
$InputButton.remove();
|
|
||||||
|
|
||||||
//Clear all upload notifications
|
|
||||||
this.resetNotif();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
feedback(sType, sMsg) {
|
|
||||||
let sColor = 'black';
|
|
||||||
let sIcon = '';
|
|
||||||
switch(sType) {
|
|
||||||
case 'error': sColor = 'red'; sIcon = '\u274C'; break;
|
|
||||||
case 'warning': sColor = 'orange'; sIcon = '\u26A0'; break;
|
|
||||||
case 'info': sColor = '#2D5E38'; sIcon = '\u2713'; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var sFormattedMsg = sIcon+' '+sMsg+(sMsg.slice(-1)=='.'?'':'.');
|
|
||||||
console.log(sFormattedMsg);
|
|
||||||
|
|
||||||
this.$Feedback.append($('<p>', {'style': 'color: '+sColor+';'}).text(sFormattedMsg));
|
|
||||||
this.$Feedback.scrollTop(this.$Feedback.prop("scrollHeight"));
|
|
||||||
}
|
|
||||||
|
|
||||||
incProgress() {
|
|
||||||
if(!this.progress.current) {
|
|
||||||
this.progress.$Done = $('<div>', {'style':'overflow:hidden; background:#2D5E38; color: white; text-align:right;'});
|
|
||||||
this.progress.$Left = $('<div>', {'style':'overflow:hidden; background:white; color: #2D5E38; text-align:left;'});
|
|
||||||
this.progress.$Box = $('<div>', {'id':'ggu-progress', 'style':'margin-top:1em;'})
|
|
||||||
.append($('<div>', {'style':'display:flex; width:calc(100% + 64px); margin-left:-32px; border-radius:3px; border: 1px solid #2D5E38; box-sizing: border-box;'})
|
|
||||||
.append(this.progress.$Done)
|
|
||||||
.append(this.progress.$Left)
|
|
||||||
);
|
|
||||||
this.$Feedback.before(this.progress.$Box);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.progress.current++;
|
|
||||||
|
|
||||||
if(this.progress.current < this.progress.total) {
|
|
||||||
let iRatio = Math.round(this.progress.current / this.progress.total * 100);
|
|
||||||
let sTextDone = '', sTextLeft = '';
|
|
||||||
if(iRatio < 50) {
|
|
||||||
sTextDone = '';
|
|
||||||
sTextLeft = ' '+iRatio+'% ('+this.progress.current+' / '+this.progress.total+')';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sTextDone = '('+this.progress.current+' / '+this.progress.total+') '+iRatio+'% ';
|
|
||||||
sTextLeft = '';
|
|
||||||
}
|
|
||||||
this.progress.$Done.css('flex', iRatio+' 1 0%').html(sTextDone);
|
|
||||||
this.progress.$Left.css('flex', (100 - iRatio)+' 1 0%').html(sTextLeft);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.progress.$Box.remove();
|
|
||||||
this.progress = {current:0, total:0};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Marking all upload notifications as read
|
|
||||||
resetNotif() {
|
|
||||||
this.feedback('info', 'Marking all upload notifications as read');
|
|
||||||
$.get(Gaia.URL+'/social/notifications/popup/').done((asNotifs) => {
|
|
||||||
for(var i in asNotifs) {
|
|
||||||
if(!asNotifs[i].isViewed && asNotifs[i].html.indexOf('has completed') != -1) {
|
|
||||||
$.post(Gaia.URL+'/social/notifications/'+asNotifs[i].id+'/markviewed/');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Parse files from input (multiple)
|
|
||||||
readInputFiles() {
|
|
||||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob) {
|
|
||||||
this.feedback('error', 'File APIs are not fully supported in this browser');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else this.feedback('info', 'Parsing input files');
|
|
||||||
|
|
||||||
//Get Folder Name (text input)
|
|
||||||
let aoReaders = [];
|
|
||||||
let iCount = 0;
|
|
||||||
this.asFiles = this.$InputFile.prop('files');
|
|
||||||
for(var i=0 ; i < this.asFiles.length ; i++) {
|
|
||||||
aoReaders[i] = new FileReader();
|
|
||||||
aoReaders[i].onload = ((oFileReader) => {
|
|
||||||
return (asResult) => {
|
|
||||||
iCount++;
|
|
||||||
this.feedback('info', 'Reading file "'+oFileReader.name+'" ('+iCount+'/'+this.asFiles.length+')');
|
|
||||||
this.parseFile(oFileReader.name, asResult.target.result);
|
|
||||||
this.asFolders[oFileReader.name] = {};
|
|
||||||
|
|
||||||
if(iCount == this.asFiles.length) {
|
|
||||||
this.progress.total = this.aoTracks.length + this.aoWaypoints.length + this.asFiles.length; //extra action per file: Create folder
|
|
||||||
this.createFolders();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})(this.asFiles[i]);
|
|
||||||
aoReaders[i].readAsText(this.asFiles[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Parse GPX files to consolidate tracks & waypoints
|
|
||||||
parseFile(sFileName, oContent) {
|
|
||||||
this.feedback('info', 'Parsing file "'+sFileName+'"');
|
|
||||||
|
|
||||||
var oGPX = new gpxParser();
|
|
||||||
oGPX.parse(oContent);
|
|
||||||
|
|
||||||
//Waypoints
|
|
||||||
for(var w in oGPX.waypoints) {
|
|
||||||
oGPX.waypoints[w].filename = sFileName;
|
|
||||||
var sWaypointName = oGPX.waypoints[w].name;
|
|
||||||
/* if(sWaypointName.indexOf('Milestone - ') != -1 && sWaypointName.substr(-2) != '00') { //1 milestone every 100 miles
|
|
||||||
this.feedback('info', 'Ignoring milestone waypoint "'+sWaypointName+'"');
|
|
||||||
}
|
|
||||||
else */this.aoWaypoints.push(oGPX.waypoints[w]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Tracks
|
|
||||||
for(var t in oGPX.tracks) {
|
|
||||||
oGPX.tracks[t].filename = sFileName;
|
|
||||||
this.aoTracks.push(oGPX.tracks[t]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Delete existing folder with same name & recreating it
|
|
||||||
createFolders() {
|
|
||||||
let iCount = 0;
|
|
||||||
$.each(this.asFolders, (sFileName, asFolder) => {
|
|
||||||
|
|
||||||
//Folder Name
|
|
||||||
let sFolderName = sFileName.replace(/\.[^\.]+$/, '');
|
|
||||||
|
|
||||||
this.feedback('info', 'Looking for existing folder "'+sFolderName+'"...');
|
|
||||||
$.get(Gaia.API+'/folder/?search='+sFolderName).done((asFolders) => {
|
|
||||||
if(asFolders != '' && !$.isEmptyObject(asFolders)) {
|
|
||||||
for(var f in asFolders) {
|
|
||||||
this.feedback('info', 'Deleting "'+asFolders[f].title+'" folder');
|
|
||||||
$.ajax({
|
|
||||||
url: Gaia.API+'/folder/'+asFolders[f].id+'/',
|
|
||||||
type: 'DELETE'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else this.feedback('info', 'No folder named "'+sFolderName+'" found');
|
|
||||||
|
|
||||||
this.feedback('info', 'Creating folder "'+sFolderName+'"');
|
|
||||||
let sTime = (new Date()).toISOString();
|
|
||||||
$.post({
|
|
||||||
url: Gaia.API+'/folder/',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: JSON.stringify({
|
|
||||||
title: sFolderName,
|
|
||||||
imported: true
|
|
||||||
})
|
|
||||||
}).done((asFolder) => {
|
|
||||||
this.feedback('info', 'Folder "'+asFolder.properties.name+'" created');
|
|
||||||
this.asFolders[sFileName] = asFolder;
|
|
||||||
this.incProgress();
|
|
||||||
|
|
||||||
iCount++;
|
|
||||||
if(iCount == Object.keys(this.asFolders).length) this.uploadTrack();
|
|
||||||
}).fail(() => {
|
|
||||||
this.feedback('error', 'Folder "'+sFolderName+'" could not be created');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Build & Upload Track File
|
|
||||||
uploadTrack(iIndex) {
|
|
||||||
iIndex = iIndex || 0;
|
|
||||||
|
|
||||||
if(iIndex == 0) this.feedback('info', 'Uploading tracks...');
|
|
||||||
|
|
||||||
let aoTrack = this.aoTracks[iIndex];
|
|
||||||
this.feedback('info', 'Uploading track "'+aoTrack.name+'"');
|
|
||||||
|
|
||||||
let sColor = '#4ABD32';
|
|
||||||
switch(aoTrack.color) {
|
|
||||||
|
|
||||||
//Personal Colors
|
|
||||||
case 'DarkBlue': sColor = '#2D3FC7'; break;
|
|
||||||
case 'Magenta': sColor = '#B60DC3'; break;
|
|
||||||
|
|
||||||
//Garmin Colors
|
|
||||||
case 'Black': sColor = '#000000'; break;
|
|
||||||
case 'DarkRed': sColor = '#F90553'; break;
|
|
||||||
case 'DarkGreen': sColor = '#009B89'; break;
|
|
||||||
case 'DarkYellow': sColor = '#DCEE0E'; break;
|
|
||||||
//case 'DarkBlue': sColor = '#5E23CA'; break;
|
|
||||||
case 'DarkMagenta': sColor = '#B60DC3'; break;
|
|
||||||
case 'DarkCyan': sColor = '#00ACF8'; break;
|
|
||||||
case 'LightGray': sColor = '#A4A4A4'; break;
|
|
||||||
case 'DarkGray': sColor = '#577B8E'; break;
|
|
||||||
case 'Red': sColor = '#F90553'; break;
|
|
||||||
case 'Green': sColor = '#36C03B'; break;
|
|
||||||
case 'Yellow': sColor = '#FFF011'; break;
|
|
||||||
case 'Blue': sColor = '#2D3FC7'; break;
|
|
||||||
//case 'Magenta': sColor = '#B60DC3'; break;
|
|
||||||
case 'Cyan': sColor = '#00C3DD'; break;
|
|
||||||
case 'White': sColor = '#FFFFFF'; break;
|
|
||||||
case 'Transparent': sColor = '#784D3E'; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add track points
|
|
||||||
let aoCoords = [];
|
|
||||||
for(var p in aoTrack.points) {
|
|
||||||
let pt = aoTrack.points[p];
|
|
||||||
aoCoords.push([pt.lon, pt.lat, pt.ele, 0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Convert to geojson
|
|
||||||
let sPostedData = JSON.stringify({
|
|
||||||
geometry: {
|
|
||||||
coordinates: [aoCoords],
|
|
||||||
type: 'MultiLineString'
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
archived: false,
|
|
||||||
color: sColor,
|
|
||||||
//distance: 168405.62350073704,
|
|
||||||
filename: aoTrack.filename,
|
|
||||||
hexcolor: sColor,
|
|
||||||
isLatestImport: true,
|
|
||||||
isLocallyCreated: true,
|
|
||||||
isPublicTrack: false,
|
|
||||||
isValid: true,
|
|
||||||
localId: 'track'+(iIndex + 1),
|
|
||||||
notes: aoTrack.desc,
|
|
||||||
parent_folder_id: this.asFolders[aoTrack.filename].id,
|
|
||||||
routing_mode: null,
|
|
||||||
time_created: aoTrack.time || (new Date()).toISOString(),
|
|
||||||
title: aoTrack.name,
|
|
||||||
type: 'track',
|
|
||||||
writable: true
|
|
||||||
},
|
|
||||||
type: 'Feature'
|
|
||||||
});
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
$.post({
|
|
||||||
url: Gaia.API+'/track/',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: sPostedData,
|
|
||||||
trackName: aoTrack.name
|
|
||||||
}).done(function(asTrack) {
|
|
||||||
self.aoTracks[iIndex] = asTrack;
|
|
||||||
self.confirmTrack(iIndex, asTrack, this.data);
|
|
||||||
}).fail(function() {
|
|
||||||
self.feedback('error', 'Track "'+this.trackName+'" upload failed (stage 1). Retrying...');
|
|
||||||
self.uploadTrack(iIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
confirmTrack(iIndex, asTrack, sPostedData) {
|
|
||||||
iIndex = iIndex || 0;
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
$.ajax({
|
|
||||||
url: Gaia.API+'/track/'+asTrack.features[0].id+'/',
|
|
||||||
type: 'PUT',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: sPostedData,
|
|
||||||
trackName: asTrack.features[0].properties.title
|
|
||||||
}).done(function() {
|
|
||||||
self.feedback('info', 'Track "'+this.trackName+'" uploaded');
|
|
||||||
self.incProgress();
|
|
||||||
iIndex++;
|
|
||||||
if(iIndex < self.aoTracks.length) self.uploadTrack(iIndex);
|
|
||||||
else {
|
|
||||||
self.feedback('info', 'All tracks uploaded');
|
|
||||||
self.uploadWayPoints();
|
|
||||||
}
|
|
||||||
}).fail(function() {
|
|
||||||
self.feedback('error', 'Track "'+this.trackName+'" upload failed (stage 2). Retrying...');
|
|
||||||
self.confirmTrack(iIndex, asTrack, sPostedData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
//Wait for file to be processed by Gaia
|
|
||||||
checkNotif() {
|
|
||||||
this.feedback('info', 'Waiting for Gaia to process consolidated track');
|
|
||||||
$.get(Gaia.URL+'/social/notifications/popup/').done((asNotifs) => {
|
|
||||||
for(var i in asNotifs) {
|
|
||||||
if(!asNotifs[i].isViewed && asNotifs[i].html.indexOf('has completed') != -1) {
|
|
||||||
this.feedback('info', 'Notification '+asNotifs[i].id+' found. Marking as read');
|
|
||||||
var $Notif = $('<span>').html(asNotifs[i].html);
|
|
||||||
this.sFolderId = $Notif.find('a').attr('href').split('/')[3];
|
|
||||||
$.post(Gaia.URL+'/social/notifications/'+asNotifs[i].id+'/markviewed/');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.sFolderId != '') {
|
|
||||||
this.setTracksColor();
|
|
||||||
this.uploadWayPoints();
|
|
||||||
}
|
|
||||||
else setTimeout((() => {this.checkNotif();}), 1000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
uploadWayPoints(iIndex) {
|
|
||||||
iIndex = iIndex || 0;
|
|
||||||
|
|
||||||
//Upload waypoints
|
|
||||||
var sWaypointName = this.aoWaypoints[iIndex].name;
|
|
||||||
var aoWaypoint = this.aoWaypoints[iIndex];
|
|
||||||
|
|
||||||
this.feedback('info', 'Uploading waypoint '+(iIndex + 1)+'/'+this.aoWaypoints.length+' ('+aoWaypoint.name+')');
|
|
||||||
var asPost = {
|
|
||||||
geometry: {
|
|
||||||
coordinates: [
|
|
||||||
aoWaypoint.lon,
|
|
||||||
aoWaypoint.lat,
|
|
||||||
aoWaypoint.ele
|
|
||||||
],
|
|
||||||
type: 'Point'
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
archived: false,
|
|
||||||
filename: aoWaypoint.filename,
|
|
||||||
icon: Gaia.getIconName(aoWaypoint.sym),
|
|
||||||
isLatestImport: true,
|
|
||||||
isLocallyCreated: true,
|
|
||||||
isValid: true,
|
|
||||||
localId: iIndex+'',
|
|
||||||
notes: aoWaypoint.desc,
|
|
||||||
parent_folder_id: this.asFolders[aoWaypoint.filename].id,
|
|
||||||
time_created: aoWaypoint.time || (new Date()).toISOString(),
|
|
||||||
title: aoWaypoint.name,
|
|
||||||
type: 'waypoint',
|
|
||||||
writable: true
|
|
||||||
},
|
|
||||||
type: 'Feature'
|
|
||||||
};
|
|
||||||
|
|
||||||
let sData = JSON.stringify(asPost);
|
|
||||||
var self = this;
|
|
||||||
$.post({
|
|
||||||
url: Gaia.API+'/waypoint/',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: sData
|
|
||||||
}).done(function(asWaypoint) {
|
|
||||||
self.aoWaypoints[iIndex] = asWaypoint;
|
|
||||||
self.confirmWayPoint(iIndex, asWaypoint, this.data);
|
|
||||||
}).fail(function(){
|
|
||||||
self.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' "'+sWaypointName+'" (Stage 1). Trying again...');
|
|
||||||
self.uploadWayPoints(iIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
confirmWayPoint(iIndex, asWaypoint, sPostedData) {
|
|
||||||
$.ajax({
|
|
||||||
url: Gaia.API+'/waypoint/'+asWaypoint.properties.id+'/',
|
|
||||||
type: 'PUT',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: sPostedData
|
|
||||||
}).done(() => {
|
|
||||||
iIndex++;
|
|
||||||
this.incProgress();
|
|
||||||
if(iIndex < this.aoWaypoints.length) this.uploadWayPoints(iIndex);
|
|
||||||
//else this.assignElementsToFolders();
|
|
||||||
else this.feedback('info', 'Done');
|
|
||||||
}).fail(() => {
|
|
||||||
this.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' "'+asWaypoint.properties.title+'" (Stage 2). Trying again...');
|
|
||||||
this.confirmWayPoint(iIndex, asWaypoint, sPostedData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
assignElementsToFolders(iIndex) {
|
|
||||||
iIndex = iIndex || 0;
|
|
||||||
let asFolders = Object.keys(this.asFolders).map(key => this.asFolders[key]);
|
|
||||||
let asFolder = asFolders[iIndex];
|
|
||||||
|
|
||||||
this.feedback('info', 'Assigning elements of folder "'+asFolder.properties.name+'"');
|
|
||||||
|
|
||||||
//Folder metadata
|
|
||||||
let asData = {
|
|
||||||
cover_photo_id: asFolder.properties.cover_photo_id,
|
|
||||||
id: asFolder.id,
|
|
||||||
name: asFolder.properties.name,
|
|
||||||
notes: asFolder.properties.notes,
|
|
||||||
time_created: asFolder.properties.time_created,
|
|
||||||
updated_date: asFolder.properties.updated_date
|
|
||||||
}
|
|
||||||
|
|
||||||
//Assign waypoints to folder
|
|
||||||
asData.waypoints = [];
|
|
||||||
for(var w in this.aoWaypoints) {
|
|
||||||
if(this.aoWaypoints[w].parent_folder_id = asFolder.id) asData.waypoints.push(this.aoWaypoints[w].properties.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Assign tracks to folder
|
|
||||||
asData.tracks = [];
|
|
||||||
for(var t in this.aoTracks) {
|
|
||||||
if(this.aoTracks[t].parent_folder_id = asFolder.id) asData.tracks.push(this.aoTracks[t].features[0].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: Gaia.API+'/folder/'+asFolder.id+'/',
|
|
||||||
type: 'PUT',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: JSON.stringify(asData)
|
|
||||||
}).done(() => {
|
|
||||||
iIndex++;
|
|
||||||
this.incProgress();
|
|
||||||
this.feedback('info', 'Tracks & waypoints assigned to folder "'+asFolder.properties.name+'"');
|
|
||||||
if(iIndex < asFolders.length) this.assignElementsToFolders(iIndex);
|
|
||||||
else this.feedback('info', 'Done');
|
|
||||||
}).fail(() => {
|
|
||||||
this.feedback('warning', 'Failed to assign waypoints & tracks to folder "'+asFolder.properties.name+'". Trying again...');
|
|
||||||
this.assignElementsToFolders(iIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
static getIconName(sGarminName) {
|
|
||||||
var asMapping = {
|
|
||||||
'Bridge': 'bridge',
|
|
||||||
'Campground': 'campsite-24',
|
|
||||||
'Car': 'car-24',
|
|
||||||
'Cemetery': 'cemetery-24',
|
|
||||||
'Church': 'ghost-town',
|
|
||||||
'City (Capitol)': 'city-24',
|
|
||||||
'Convenience Store': 'market',
|
|
||||||
'Drinking Water': 'potable-water',
|
|
||||||
'Flag, Blue': 'blue-pin-down',
|
|
||||||
'Flag, Green': 'green-pin',
|
|
||||||
'Flag, Red': 'red-pin-down',
|
|
||||||
'Forest': 'forest',
|
|
||||||
'Ground Transportation': 'car-24',
|
|
||||||
'Lodging': 'lodging-24',
|
|
||||||
'Park': 'park-24',
|
|
||||||
'Pharmacy': 'hospital-24',
|
|
||||||
'Picnic Area': 'picnic',
|
|
||||||
'Post Office': 'resupply',
|
|
||||||
'Powerline': 'petroglyph',
|
|
||||||
'Residence': 'building-24',
|
|
||||||
'Restaurant': 'restaurant-24',
|
|
||||||
'Restroom': 'toilets-24',
|
|
||||||
'Shopping Center': 'market',
|
|
||||||
'Ski Resort': 'skiing-24',
|
|
||||||
'Summit': 'peak',
|
|
||||||
'Toll Booth': 'ranger-station',
|
|
||||||
'Trail Head': 'known-route',
|
|
||||||
'Truck': 'car-24',
|
|
||||||
'Water Source': 'water-24'
|
|
||||||
};
|
|
||||||
return (sGarminName in asMapping)?asMapping[sGarminName]:'red-pin-down';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Loading GaiaGps Uploader '+GM_info.script.version);
|
|
||||||
|
|
||||||
let oGaia = new Gaia();
|
|
||||||
|
|
||||||
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
|
|
||||||
|
|
||||||
let observer = new MutationObserver((mutations, observer) => {
|
|
||||||
|
|
||||||
/* FIXME: adapts on GaiaGPS upgrade */
|
|
||||||
let $Import = $('div[aria-label="Import Data"]');
|
|
||||||
if($Import.length > 0) {
|
|
||||||
observer.disconnect();
|
|
||||||
$Import.parent('li').on('click', () => { setTimeout(() => { oGaia.setLayout(); }, 500)});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(document, { subtree: true, attributes: true});
|
|
||||||
168
lib/Controller.php
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Franzz\Spot;
|
||||||
|
|
||||||
|
use Franzz\Objects\PhpObject;
|
||||||
|
use Franzz\Objects\ToolBox;
|
||||||
|
|
||||||
|
//TODO Keep only local specificities and move bulk to Franzz\Objects\Controller
|
||||||
|
class Controller extends PhpObject
|
||||||
|
{
|
||||||
|
const MUTATING_ACTIONS = array(
|
||||||
|
'add_post',
|
||||||
|
'subscribe',
|
||||||
|
'unsubscribe',
|
||||||
|
'update_project',
|
||||||
|
'upload',
|
||||||
|
'add_comment',
|
||||||
|
'add_position',
|
||||||
|
'admin_set',
|
||||||
|
'admin_create',
|
||||||
|
'admin_delete',
|
||||||
|
'build_geojson'
|
||||||
|
);
|
||||||
|
|
||||||
|
private Spot $oSpot;
|
||||||
|
private array $asReq;
|
||||||
|
private string $sCsrfToken = '';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct(__CLASS__);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setReqVal(string $sKey, $oValue, string $sValidation=''): void
|
||||||
|
{
|
||||||
|
$this->asReq[$sKey] = $this->validateValue($sValidation, $oValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle($sProcessPage, array $argv = array()): string
|
||||||
|
{
|
||||||
|
//Start buffering so warnings/notices can be collected
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
//Parse variables
|
||||||
|
$asReq = ToolBox::getRequest($argv);
|
||||||
|
$this->asReq = array();
|
||||||
|
$sAction = $asReq['a'] ?? '';
|
||||||
|
$this->setReqVal('t', $asReq['t'] ?? '');
|
||||||
|
$this->setReqVal('name', $asReq['name'] ?? '');
|
||||||
|
$this->setReqVal('content', $asReq['content'] ?? '');
|
||||||
|
$this->setReqVal('id_project', $asReq['id_project'] ?? 0, 'positiveInt');
|
||||||
|
$this->setReqVal('id', $asReq['id'] ?? 0);
|
||||||
|
$this->setReqVal('id_entity', $asReq['id'] ?? 0, 'positiveInt');
|
||||||
|
$this->setReqVal('field', $asReq['field'] ?? '');
|
||||||
|
$this->setReqVal('value', $asReq['value'] ?? '');
|
||||||
|
$this->setReqVal('type', $asReq['type'] ?? '');
|
||||||
|
$this->setReqVal('email', $asReq['email'] ?? '');
|
||||||
|
$this->setReqVal('latitude', $asReq['latitude'] ?? '');
|
||||||
|
$this->setReqVal('longitude', $asReq['longitude'] ?? '');
|
||||||
|
$this->setReqVal('timestamp', $asReq['timestamp'] ?? 0, 'positiveInt');
|
||||||
|
$this->setReqVal('csrf_token', $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ($_POST['csrf_token'] ?? ''));
|
||||||
|
|
||||||
|
//Create Spot Instance
|
||||||
|
$this->oSpot = new Spot($sProcessPage, $this->asReq['t']);
|
||||||
|
$this->oSpot->setProjectId($this->asReq['id_project']);
|
||||||
|
|
||||||
|
//Validate CSRF & dispatch
|
||||||
|
if(!$this->validateMutationRequest($sAction)) $sResult = Spot::getJsonResult(false, Spot::UNAUTHORIZED);
|
||||||
|
elseif($sAction == '') $sResult = $this->oSpot->getAppMainPage($this->getCsrfToken());
|
||||||
|
else $sResult = $this->dispatch($sAction);
|
||||||
|
|
||||||
|
//Clean errors
|
||||||
|
$sDebug = ob_get_clean();
|
||||||
|
if($sDebug != '') $this->oSpot->addUncaughtError($sDebug);
|
||||||
|
|
||||||
|
return $sResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateMutationRequest(string $sAction): bool
|
||||||
|
{
|
||||||
|
return
|
||||||
|
PHP_SAPI === 'cli'
|
||||||
|
||
|
||||||
|
!in_array($sAction, self::MUTATING_ACTIONS, true)
|
||||||
|
||
|
||||||
|
($_SERVER['REQUEST_METHOD'] ?? '') === 'POST' && $this->checkCsrfToken($this->asReq['csrf_token'])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCsrfToken(): string
|
||||||
|
{
|
||||||
|
if($this->sCsrfToken === '') $this->initCsrfToken();
|
||||||
|
return $this->sCsrfToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setCsrfToken(): void
|
||||||
|
{
|
||||||
|
if(empty($_SESSION['csrf_token'])) $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||||
|
$this->sCsrfToken = $_SESSION['csrf_token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initCsrfToken(): void
|
||||||
|
{
|
||||||
|
if(PHP_SAPI === 'cli') return;
|
||||||
|
|
||||||
|
$bCloseSession = false;
|
||||||
|
if(session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
|
$bSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
|
||||||
|
session_set_cookie_params(array('httponly' => true, 'secure' => $bSecure, 'samesite' => 'Lax'));
|
||||||
|
session_start();
|
||||||
|
$bCloseSession = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setCsrfToken();
|
||||||
|
if($bCloseSession) session_write_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkCsrfToken(string $sClientToken): bool
|
||||||
|
{
|
||||||
|
$sServerToken = $this->getCsrfToken();
|
||||||
|
return PHP_SAPI === 'cli' || ($sServerToken !== '' && is_string($sClientToken) && hash_equals($sServerToken, $sClientToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dispatch(string $sAction): string
|
||||||
|
{
|
||||||
|
return match($sAction) {
|
||||||
|
'markers' => $this->oSpot->getMarkers(),
|
||||||
|
'last_update' => $this->oSpot->getLastUpdate(),
|
||||||
|
'geojson' => $this->oSpot->getProjectGeoJson(),
|
||||||
|
'next_feed' => $this->oSpot->getNextFeed($this->asReq['id']),
|
||||||
|
'new_feed' => $this->oSpot->getNewFeed($this->asReq['id']),
|
||||||
|
'add_post' => $this->oSpot->addPost($this->asReq['name'], $this->asReq['content']),
|
||||||
|
'subscribe' => $this->oSpot->subscribe($this->asReq['email'], $this->asReq['name']),
|
||||||
|
'unsubscribe' => $this->oSpot->unsubscribe(),
|
||||||
|
'unsubscribe_email' => $this->oSpot->unsubscribeFromEmail($this->asReq['id_entity']),
|
||||||
|
'update_project' => $this->oSpot->updateProject(),
|
||||||
|
default => $this->dispatchAdmin($sAction)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dispatchAdmin(string $sAction): string
|
||||||
|
{
|
||||||
|
if(!$this->oSpot->checkUserClearance(User::CLEARANCE_ADMIN)) {
|
||||||
|
return Spot::getJsonResult(false, Spot::NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
return match($sAction) {
|
||||||
|
'upload' => $this->oSpot->upload(),
|
||||||
|
'add_comment' => $this->oSpot->addComment($this->asReq['id_entity'], $this->asReq['content']),
|
||||||
|
'add_position' => $this->oSpot->addPosition($this->asReq['latitude'], $this->asReq['longitude'], $this->asReq['timestamp']),
|
||||||
|
'admin_get' => $this->oSpot->getAdminSettings(),
|
||||||
|
'admin_set' => $this->oSpot->setAdminSettings($this->asReq['type'], $this->asReq['id_entity'], $this->asReq['field'], $this->asReq['value']),
|
||||||
|
'admin_create' => $this->oSpot->createAdminSettings($this->asReq['type']),
|
||||||
|
'admin_delete' => $this->oSpot->deleteAdminSettings($this->asReq['type'], $this->asReq['id_entity']),
|
||||||
|
'sql' => $this->oSpot->getDbBuildScript(),
|
||||||
|
'build_geojson' => $this->oSpot->buildGeoJSON($this->asReq['name']),
|
||||||
|
default => Spot::getJsonResult(false, Spot::NOT_FOUND)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function validateValue(string $sValidation, $oValue=0)
|
||||||
|
{
|
||||||
|
return match($sValidation) {
|
||||||
|
'' => $oValue,
|
||||||
|
'positiveInt' => filter_var($oValue, FILTER_VALIDATE_INT, array('options' => array('default' => 0, 'min_range' => 0)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,12 +29,15 @@ class Converter extends PhpObject {
|
|||||||
$oGeoJson->sortOffTracks();
|
$oGeoJson->sortOffTracks();
|
||||||
$oGeoJson->saveFile();
|
$oGeoJson->saveFile();
|
||||||
|
|
||||||
return $oGpx->getLog().'<br />'.$oGeoJson->getLog();
|
return [
|
||||||
|
'logs' => $oGpx->getLog().'<br />'.$oGeoJson->getLog(),
|
||||||
|
'center' => $oGeoJson->getCenter()
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function isGeoJsonValid($sCodeName) {
|
public static function isGeoJsonValid($sCodeName) {
|
||||||
$sGpxFilePath = Gpx::getFilePath($sCodeName);
|
$sGpxFilePath = Gpx::getBackendFilePath($sCodeName);
|
||||||
$sGeoJsonFilePath = GeoJson::getFilePath($sCodeName);
|
$sGeoJsonFilePath = GeoJson::getBackendFilePath($sCodeName);
|
||||||
|
|
||||||
//No need to generate if gpx is missing
|
//No need to generate if gpx is missing
|
||||||
return !file_exists($sGpxFilePath) || file_exists($sGeoJsonFilePath) && filemtime($sGeoJsonFilePath) >= filemtime($sGpxFilePath);
|
return !file_exists($sGpxFilePath) || file_exists($sGeoJsonFilePath) && filemtime($sGeoJsonFilePath) >= filemtime($sGpxFilePath);
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ class Email extends PhpObject {
|
|||||||
$oPHPMailer->Password = Settings::MAIL_PASS; //SMTP password
|
$oPHPMailer->Password = Settings::MAIL_PASS; //SMTP password
|
||||||
$oPHPMailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; //Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` encouraged
|
$oPHPMailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; //Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` encouraged
|
||||||
$oPHPMailer->Port = 587; //TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above
|
$oPHPMailer->Port = 587; //TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above
|
||||||
$oPHPMailer->setFrom(Settings::MAIL_FROM, 'Spotty');
|
$oPHPMailer->setFrom(Settings::MAIL_FROM, Spot::PROJECT_NAME);
|
||||||
$oPHPMailer->addReplyTo(Settings::MAIL_FROM, 'Spotty');
|
$oPHPMailer->addReplyTo(Settings::MAIL_FROM, Spot::PROJECT_NAME);
|
||||||
|
|
||||||
$bSuccess = true;
|
$bSuccess = true;
|
||||||
foreach($this->asDests as $asDest) {
|
foreach($this->asDests as $asDest) {
|
||||||
|
|||||||
23
lib/Geo.php
@@ -4,26 +4,29 @@ namespace Franzz\Spot;
|
|||||||
use Franzz\Objects\PhpObject;
|
use Franzz\Objects\PhpObject;
|
||||||
use \Settings;
|
use \Settings;
|
||||||
|
|
||||||
class Geo extends PhpObject {
|
abstract class Geo extends PhpObject {
|
||||||
|
protected const EXT = '';
|
||||||
|
|
||||||
const GEO_FOLDER = '../geo/';
|
const GEO_FOLDER = 'geo';
|
||||||
const OPT_SIMPLE = 'simplification';
|
const OPT_SIMPLE = 'simplification';
|
||||||
|
|
||||||
protected $asTracks;
|
protected array $asTracks;
|
||||||
protected $sFilePath;
|
protected string $sFilePath;
|
||||||
|
|
||||||
public function __construct($sCodeName) {
|
public function __construct(string $sCodeName) {
|
||||||
parent::__construct(get_class($this), Settings::DEBUG, PhpObject::MODE_HTML);
|
parent::__construct(get_class($this), Settings::DEBUG, PhpObject::MODE_HTML);
|
||||||
$this->sFilePath = self::getFilePath($sCodeName);
|
$this->sFilePath = self::getBackEndFilePath($sCodeName);
|
||||||
$this->asTracks = array();
|
$this->asTracks = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getFilePath($sCodeName) {
|
//Access from backend
|
||||||
return self::GEO_FOLDER.$sCodeName.static::EXT;
|
public static function getBackendFilePath(string $sCodeName) {
|
||||||
|
return '../resources/'.self::GEO_FOLDER.'/'.$sCodeName.static::EXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getDistFilePath($sCodeName) {
|
//Access from frontend (/public/geo is a symlink of /resources/geo)
|
||||||
return 'geo/'.$sCodeName.static::EXT;
|
public static function getFrontendFilePath(string $sCodeName) {
|
||||||
|
return self::GEO_FOLDER.'/'.$sCodeName.static::EXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLog() {
|
public function getLog() {
|
||||||
|
|||||||
@@ -77,6 +77,11 @@ class GeoJson extends Geo {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if($sType != 'hitchhiking' && str_contains($asTrackProps['desc'], ' ➜ ')) {
|
||||||
|
list($sFrom, $sTo) = explode(' ➜ ', $asTrackProps['desc']);
|
||||||
|
$asTrack['properties']['leg'] = ['from'=> $sFrom, 'to'=> $sTo];
|
||||||
|
}
|
||||||
|
|
||||||
//Track points
|
//Track points
|
||||||
$asTrackPoints = $asTrackProps['points'];
|
$asTrackPoints = $asTrackProps['points'];
|
||||||
$iPointCount = count($asTrackPoints);
|
$iPointCount = count($asTrackPoints);
|
||||||
@@ -103,7 +108,6 @@ class GeoJson extends Geo {
|
|||||||
if($bSimplify) $this->addNotice('Total: '.$iGlobalInvalidPointCount.'/'.$iGlobalPointCount.' points removed ('.round($iGlobalInvalidPointCount / $iGlobalPointCount * 100, 1).'%)');
|
if($bSimplify) $this->addNotice('Total: '.$iGlobalInvalidPointCount.'/'.$iGlobalPointCount.' points removed ('.round($iGlobalInvalidPointCount / $iGlobalPointCount * 100, 1).'%)');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function sortOffTracks() {
|
public function sortOffTracks() {
|
||||||
$this->addNotice('Sorting off-tracks');
|
$this->addNotice('Sorting off-tracks');
|
||||||
|
|
||||||
@@ -155,7 +159,19 @@ class GeoJson extends Geo {
|
|||||||
$this->asTracks = array_values($asTracks);
|
$this->asTracks = array_values($asTracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseOptions($sComment){
|
public function getCenter() {
|
||||||
|
$asCoords = array();
|
||||||
|
$asMainTracks = array_filter($this->asTracks, function ($astrack) {return $astrack['properties']['type'] == 'main';});
|
||||||
|
foreach($asMainTracks as $asMainTrack) {
|
||||||
|
foreach($asMainTrack['geometry']['coordinates'] as $aiCoords) {
|
||||||
|
$asCoords[] = $aiCoords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $asCoords[(int) floor(count($asCoords) / 2)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseOptions($sComment) {
|
||||||
$sComment = strip_tags(html_entity_decode($sComment));
|
$sComment = strip_tags(html_entity_decode($sComment));
|
||||||
$asOptions = array(self::OPT_SIMPLE=>'');
|
$asOptions = array(self::OPT_SIMPLE=>'');
|
||||||
foreach(explode("\n", $sComment) as $sLine) {
|
foreach(explode("\n", $sComment) as $sLine) {
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ class Media extends PhpObject {
|
|||||||
//DB Tables
|
//DB Tables
|
||||||
const MEDIA_TABLE = 'medias';
|
const MEDIA_TABLE = 'medias';
|
||||||
|
|
||||||
//Media folders
|
//Media folders (works because /public/files is a symlink of /files)
|
||||||
const MEDIA_FOLDER = 'files/';
|
const MEDIA_FOLDER = 'files';
|
||||||
const THUMB_FOLDER = self::MEDIA_FOLDER.'thumbs/';
|
const THUMB_FOLDER = self::MEDIA_FOLDER.'/thumbs';
|
||||||
|
|
||||||
const THUMB_MAX_WIDTH = 400;
|
const THUMB_MAX_WIDTH = 400;
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ class Media extends PhpObject {
|
|||||||
'-print_format json', //output format: json
|
'-print_format json', //output format: json
|
||||||
'-i' //input file
|
'-i' //input file
|
||||||
));
|
));
|
||||||
exec('ffprobe '.$sParams.' "'.$sMediaPath.'"', $asResult);
|
exec('ffprobe '.$sParams.' '.escapeshellarg($sMediaPath), $asResult);
|
||||||
$asExif = json_decode(implode('', $asResult), true);
|
$asExif = json_decode(implode('', $asResult), true);
|
||||||
|
|
||||||
//Taken On
|
//Taken On
|
||||||
@@ -269,10 +269,10 @@ class Media extends PhpObject {
|
|||||||
$sTempPath = self::getMediaPath(uniqid('temp_').'.png');
|
$sTempPath = self::getMediaPath(uniqid('temp_').'.png');
|
||||||
$asResult = array();
|
$asResult = array();
|
||||||
$sParams = implode(' ', array(
|
$sParams = implode(' ', array(
|
||||||
'-i "'.$sMediaPath.'"', //input file
|
'-i '.escapeshellarg($sMediaPath), //input file
|
||||||
'-ss 00:00:01.000', //Image taken after x seconds
|
'-ss 00:00:01.000', //Image taken after x seconds
|
||||||
'-vframes 1', //number of video frames to output
|
'-vframes 1', //number of video frames to output
|
||||||
'"'.$sTempPath.'"', //output file
|
escapeshellarg($sTempPath), //output file
|
||||||
));
|
));
|
||||||
exec('ffmpeg '.$sParams, $asResult);
|
exec('ffmpeg '.$sParams, $asResult);
|
||||||
|
|
||||||
@@ -288,15 +288,16 @@ class Media extends PhpObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static function getMediaPath($sMediaName, $sFileType='media') {
|
private static function getMediaPath($sMediaName, $sFileType='media') {
|
||||||
if($sFileType=='thumbnail') return self::THUMB_FOLDER.$sMediaName.(strtolower(substr($sMediaName, -3))=='mov'?'.png':'');
|
if($sFileType=='thumbnail') return self::THUMB_FOLDER.'/'.$sMediaName.(strtolower(substr($sMediaName, -3))=='mov'?'.png':'');
|
||||||
else return self::MEDIA_FOLDER.$sMediaName;
|
else return self::MEDIA_FOLDER.'/'.$sMediaName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getMediaType($sMediaName) {
|
private static function getMediaType($sMediaName) {
|
||||||
$sMediaPath = self::getMediaPath($sMediaName);
|
$sMediaPath = self::getMediaPath($sMediaName);
|
||||||
$sMediaMime = mime_content_type($sMediaPath);
|
$sMediaMime = mime_content_type($sMediaPath);
|
||||||
switch($sMediaMime) {
|
switch($sMediaMime) {
|
||||||
case 'video/quicktime': $sType = 'video'; break;
|
case 'video/quicktime':
|
||||||
|
case 'video/mp4': $sType = 'video'; break;
|
||||||
default: $sType = 'image'; break;
|
default: $sType = 'image'; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,11 +126,14 @@ class Project extends PhpObject {
|
|||||||
Db::getId(self::PROJ_TABLE)." AS id",
|
Db::getId(self::PROJ_TABLE)." AS id",
|
||||||
'codename',
|
'codename',
|
||||||
'name',
|
'name',
|
||||||
|
'latitude',
|
||||||
|
'longitude',
|
||||||
'active_from',
|
'active_from',
|
||||||
'active_to',
|
'active_to',
|
||||||
"IF(NOW() BETWEEN active_from AND active_to, 1, IF(NOW() < active_from, 0, 2)) AS mode"
|
"IF(NOW() BETWEEN active_from AND active_to, 1, IF(NOW() < active_from, 0, 2)) AS mode"
|
||||||
),
|
),
|
||||||
'from' => self::PROJ_TABLE
|
'from' => self::PROJ_TABLE,
|
||||||
|
'orderBy' => array('active_from' => 'ASC')
|
||||||
);
|
);
|
||||||
if($bSpecificProj) $asInfo['constraint'] = array(Db::getId(self::PROJ_TABLE)=>$iProjectId);
|
if($bSpecificProj) $asInfo['constraint'] = array(Db::getId(self::PROJ_TABLE)=>$iProjectId);
|
||||||
|
|
||||||
@@ -142,9 +145,7 @@ class Project extends PhpObject {
|
|||||||
case 2: $asProject['mode'] = self::MODE_HISTO; break;
|
case 2: $asProject['mode'] = self::MODE_HISTO; break;
|
||||||
}
|
}
|
||||||
$asProject['editable'] = $this->isModeEditable($asProject['mode']);
|
$asProject['editable'] = $this->isModeEditable($asProject['mode']);
|
||||||
|
$asProject['gpxfilepath'] = Spot::addTimestampToFilePath(Gpx::getFrontendFilePath($sCodeName));
|
||||||
//$asProject['geofilepath'] = Spot::addTimestampToFilePath(GeoJson::getDistFilePath($sCodeName));
|
|
||||||
$asProject['gpxfilepath'] = Spot::addTimestampToFilePath(Gpx::getDistFilePath($sCodeName));
|
|
||||||
$asProject['codename'] = $sCodeName;
|
$asProject['codename'] = $sCodeName;
|
||||||
$asProject['default'] = ($sCodeName == $sDefaultProjectCodeName);
|
$asProject['default'] = ($sCodeName == $sDefaultProjectCodeName);
|
||||||
}
|
}
|
||||||
@@ -152,9 +153,12 @@ class Project extends PhpObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getGeoJson() {
|
public function getGeoJson() {
|
||||||
if($this->sCodeName != '' && !Converter::isGeoJsonValid($this->sCodeName)) Converter::convertToGeoJson($this->sCodeName);
|
if($this->sCodeName != '' && !Converter::isGeoJsonValid($this->sCodeName)){
|
||||||
|
$aiCenter = Converter::convertToGeoJson($this->sCodeName)['center'];
|
||||||
|
$this->oDb->updateRow(self::PROJ_TABLE, $this->iProjectId, ['latitude' => $aiCenter[1], 'longitude' => $aiCenter[0]]);
|
||||||
|
}
|
||||||
|
|
||||||
return json_decode(file_get_contents(GeoJson::getDistFilePath($this->sCodeName)), true);
|
return json_decode(file_get_contents(GeoJson::getBackendFilePath($this->sCodeName)), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getProject() {
|
public function getProject() {
|
||||||
|
|||||||
102
lib/Spot.php
@@ -41,6 +41,7 @@ class Spot extends Main
|
|||||||
const MAIL_CHUNK_SIZE = 5;
|
const MAIL_CHUNK_SIZE = 5;
|
||||||
|
|
||||||
const DEFAULT_LANG = 'en';
|
const DEFAULT_LANG = 'en';
|
||||||
|
const PROJECT_NAME = 'LiveTrail';
|
||||||
|
|
||||||
const MAIN_PAGE = 'index';
|
const MAIN_PAGE = 'index';
|
||||||
|
|
||||||
@@ -163,34 +164,40 @@ class Spot extends Main
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAppMainPage() {
|
public function getAppMainPage(string $sCsrfToken='') {
|
||||||
|
|
||||||
//Cache Page List
|
|
||||||
$asPages = array_diff($this->asMasks, array('email.update', 'email.confirmation'));
|
|
||||||
if(!$this->oUser->checkUserClearance(User::CLEARANCE_ADMIN)) {
|
|
||||||
$asPages = array_diff($asPages, array('admin', 'upload'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getMainPage(
|
return parent::getMainPage(
|
||||||
array(
|
array(
|
||||||
'projects' => $this->oProject->getProjects(),
|
'projects' => $this->oProject->getProjects(),
|
||||||
'user' => $this->oUser->getUserInfo(),
|
'user' => $this->oUser->getUserInfo(),
|
||||||
'consts' => array(
|
'consts' => array(
|
||||||
'modes' => Project::MODES,
|
'modes' => Project::MODES,
|
||||||
'clearances' => User::CLEARANCES,
|
'clearances' => User::CLEARANCES,
|
||||||
'default_timezone' => Settings::TIMEZONE,
|
'default_timezone' => Settings::TIMEZONE,
|
||||||
|
'default_maps' => $this->oMap->getProjectMaps(-1),
|
||||||
'chunk_size' => self::FEED_CHUNK_SIZE,
|
'chunk_size' => self::FEED_CHUNK_SIZE,
|
||||||
'hash_sep' => '-',
|
'hash_sep' => '-',
|
||||||
'title' => 'Spotty',
|
'title' => self::PROJECT_NAME,
|
||||||
'default_page' => 'project'
|
'default_page' => 'project',
|
||||||
|
'csrf_token' => $sCsrfToken
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
self::MAIN_PAGE,
|
self::MAIN_PAGE,
|
||||||
array(
|
array(
|
||||||
'language' => $this->oLang->getLanguage(),
|
'tags' => [
|
||||||
'filepath_js' => self::addTimestampToFilePath('../dist/app.js'),
|
'language' => $this->oLang->getLanguage(),
|
||||||
),
|
'title' => self::PROJECT_NAME,
|
||||||
$asPages
|
],
|
||||||
|
'instances' => [
|
||||||
|
'entrypoint' => $this->getAppEntryPoints()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getAppEntryPoints() {
|
||||||
|
return array_map(
|
||||||
|
function($sFileName) {return ['filename' => self::addTimestampToFilePath($sFileName)];},
|
||||||
|
json_decode(file_get_contents('assets/entrypoints.json'), true)['entrypoints']['app']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,17 +269,6 @@ class Spot extends Main
|
|||||||
return $oEmail->send();
|
return $oEmail->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function genCronFile() {
|
|
||||||
//$bSuccess = (file_put_contents('spot_cron.sh', '#!/bin/bash'."\n".'cd '.dirname($_SERVER['SCRIPT_FILENAME'])."\n".'php -f index.php a=update_feed')!==false);
|
|
||||||
$sFileName = 'spot_cron.sh';
|
|
||||||
$sContent =
|
|
||||||
'#!/bin/bash'."\n".
|
|
||||||
'wget -qO- '.$this->asContext['serv_name'].'index.php?a=update_project > /dev/null'."\n".
|
|
||||||
'#Crontab job: 0 * * * * . '.dirname($_SERVER['SCRIPT_FILENAME']).'/'.$sFileName.' > /dev/null'."\n";
|
|
||||||
$bSuccess = (file_put_contents($sFileName, $sContent)!==false);
|
|
||||||
return self::getJsonResult($bSuccess, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMarkers($asMessageIds=array(), $asMediaIds=array(), $bInternal=false)
|
public function getMarkers($asMessageIds=array(), $asMediaIds=array(), $bInternal=false)
|
||||||
{
|
{
|
||||||
//Get messages
|
//Get messages
|
||||||
@@ -284,6 +280,7 @@ class Spot extends Main
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Get Geo-positioned Medias
|
//Get Geo-positioned Medias
|
||||||
|
//FIXME Make more efficient than requesting images twice from DB
|
||||||
$asMedias = $this->getMedias('taken_on', $asMediaIds);
|
$asMedias = $this->getMedias('taken_on', $asMediaIds);
|
||||||
$asGeoMedias = $this->getMedias('posted_on', $asMediaIds, true);
|
$asGeoMedias = $this->getMedias('posted_on', $asMediaIds, true);
|
||||||
foreach($asGeoMedias as &$asGeoMedia) {
|
foreach($asGeoMedias as &$asGeoMedia) {
|
||||||
@@ -292,8 +289,6 @@ class Spot extends Main
|
|||||||
|
|
||||||
$asGeoMedia['id'] = $iId;
|
$asGeoMedia['id'] = $iId;
|
||||||
$asGeoMedia['type'] = 'media';
|
$asGeoMedia['type'] = 'media';
|
||||||
$asGeoMedia['lat_dms'] = self::decToDms($asGeoMedia['latitude'], 'lat');
|
|
||||||
$asGeoMedia['lon_dms'] = self::decToDms($asGeoMedia['longitude'], 'lon');
|
|
||||||
$asGeoMedia['medias'] = array_values(array_filter($asMedias, function($asMedia) use ($iId) {
|
$asGeoMedia['medias'] = array_values(array_filter($asMedias, function($asMedia) use ($iId) {
|
||||||
return $asMedia['id_media'] == $iId;
|
return $asMedia['id_media'] == $iId;
|
||||||
}));
|
}));
|
||||||
@@ -301,8 +296,8 @@ class Spot extends Main
|
|||||||
|
|
||||||
//Assign medias to closest message
|
//Assign medias to closest message
|
||||||
if(!empty($asMessages)) {
|
if(!empty($asMessages)) {
|
||||||
usort($asMessages, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
|
usort($asMessages, function($a, $b){return (int) $a['unix_time'] <=> (int) $b['unix_time'];});
|
||||||
usort($asMedias, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
|
usort($asMedias, function($a, $b){return (int) $a['unix_time'] <=> (int) $b['unix_time'];});
|
||||||
|
|
||||||
$iIndex = 0;
|
$iIndex = 0;
|
||||||
$iMaxIndex = count($asMessages) - 1;
|
$iMaxIndex = count($asMessages) - 1;
|
||||||
@@ -323,21 +318,22 @@ class Spot extends Main
|
|||||||
|
|
||||||
//Combine markers
|
//Combine markers
|
||||||
$asMarkers = [...$asMessages, ...$asGeoMedias];
|
$asMarkers = [...$asMessages, ...$asGeoMedias];
|
||||||
usort($asMarkers, function($a, $b){return $a['unix_time'] > $b['unix_time'];});
|
usort($asMarkers, function($a, $b){return (int) $a['unix_time'] <=> (int) $b['unix_time'];});
|
||||||
|
|
||||||
//Spot Last Update
|
|
||||||
$asLastUpdate = array();
|
|
||||||
$this->addTimeStamp($asLastUpdate, $this->oProject->getLastUpdate());
|
|
||||||
|
|
||||||
$asResult = array(
|
$asResult = array(
|
||||||
'markers' => $asMarkers,
|
'markers' => $asMarkers,
|
||||||
'maps' => $this->oMap->getProjectMaps($this->oProject->getProjectId()),
|
'maps' => $this->oMap->getProjectMaps($this->oProject->getProjectId())
|
||||||
'last_update' => $asLastUpdate
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return $bInternal?$asResult:self::getJsonResult(true, '', $asResult);
|
return $bInternal?$asResult:self::getJsonResult(true, '', $asResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLastUpdate() {
|
||||||
|
$asLastUpdate = array();
|
||||||
|
$this->addTimeStamp($asLastUpdate, $this->oProject->getLastUpdate());
|
||||||
|
return self::getJsonResult(true, '', $asLastUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
public function subscribe($sEmail, $sNickName) {
|
public function subscribe($sEmail, $sNickName) {
|
||||||
$asResult = $this->oUser->addUser($sEmail, $this->oLang->getLanguage(), date_default_timezone_get(), $sNickName);
|
$asResult = $this->oUser->addUser($sEmail, $this->oLang->getLanguage(), date_default_timezone_get(), $sNickName);
|
||||||
$asUserInfo = $this->oUser->getUserInfo();
|
$asUserInfo = $this->oUser->getUserInfo();
|
||||||
@@ -428,6 +424,12 @@ class Spot extends Main
|
|||||||
$this->addTimeStamp($asMedia, strtotime($asMedia[$sTimeRefField]), $asMedia['timezone']);
|
$this->addTimeStamp($asMedia, strtotime($asMedia[$sTimeRefField]), $asMedia['timezone']);
|
||||||
$this->addTimeStamp($asMedia, strtotime($asMedia['taken_on']), $asMedia['timezone'], 'taken_on');
|
$this->addTimeStamp($asMedia, strtotime($asMedia['taken_on']), $asMedia['timezone'], 'taken_on');
|
||||||
$this->addTimeStamp($asMedia, strtotime($asMedia['posted_on']), $asMedia['timezone'], 'posted_on');
|
$this->addTimeStamp($asMedia, strtotime($asMedia['posted_on']), $asMedia['timezone'], 'posted_on');
|
||||||
|
|
||||||
|
if($asMedia['latitude'] != '' && $asMedia['longitude'] != '') {
|
||||||
|
$asMedia['lat_dms'] = self::decToDms($asMedia['latitude'], 'lat');
|
||||||
|
$asMedia['lon_dms'] = self::decToDms($asMedia['longitude'], 'lon');
|
||||||
|
}
|
||||||
|
|
||||||
unset($asMedia['taken_on']);
|
unset($asMedia['taken_on']);
|
||||||
unset($asMedia['posted_on']);
|
unset($asMedia['posted_on']);
|
||||||
}
|
}
|
||||||
@@ -557,10 +559,10 @@ class Spot extends Main
|
|||||||
return $bInternal?$asResult['feed']:self::getJsonResult(true, '', $asResult);
|
return $bInternal?$asResult['feed']:self::getJsonResult(true, '', $asResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFeed($iRefId=0, $sDirection, $sSort) {
|
private function getFeed($iRefId, $sDirection, $sSort) {
|
||||||
$this->oDb->cleanSql($iRefId);
|
$sRefId = is_scalar($iRefId) && preg_match('/^\d+(?:\.\d+)?$/D', (string) $iRefId) ? (string) $iRefId : '0';
|
||||||
$this->oDb->cleanSql($sDirection);
|
$sDirection = ($sDirection === '>')?'>':'<';
|
||||||
$this->oDb->cleanSql($sSort);
|
$sSort = ($sSort === 'ASC')?'ASC':'DESC';
|
||||||
|
|
||||||
$sProjectIdField = Db::getId(Project::PROJ_TABLE);
|
$sProjectIdField = Db::getId(Project::PROJ_TABLE);
|
||||||
$sMsgIdField = Db::getId(Feed::MSG_TABLE);
|
$sMsgIdField = Db::getId(Feed::MSG_TABLE);
|
||||||
@@ -583,7 +585,7 @@ class Spot extends Main
|
|||||||
"FROM ".self::POST_TABLE,
|
"FROM ".self::POST_TABLE,
|
||||||
$this->getFeedConstraints(self::POST_TABLE, 'site_time', 'sql'),
|
$this->getFeedConstraints(self::POST_TABLE, 'site_time', 'sql'),
|
||||||
") AS items",
|
") AS items",
|
||||||
($iRefId > 0)?("WHERE ref ".$sDirection." ".$iRefId):"",
|
($sRefId !== '0')?("WHERE ref ".$sDirection." ".$sRefId):"",
|
||||||
"ORDER BY ref ".$sSort,
|
"ORDER BY ref ".$sSort,
|
||||||
"LIMIT ".self::FEED_CHUNK_SIZE
|
"LIMIT ".self::FEED_CHUNK_SIZE
|
||||||
));
|
));
|
||||||
@@ -664,11 +666,6 @@ class Spot extends Main
|
|||||||
|
|
||||||
public function addPosition($sLat, $sLng, $iTimestamp) {
|
public function addPosition($sLat, $sLng, $iTimestamp) {
|
||||||
$oFeed = new Feed($this->oDb, $this->oProject->getFeedIds()[0]);
|
$oFeed = new Feed($this->oDb, $this->oProject->getFeedIds()[0]);
|
||||||
<<<<<<< HEAD:inc/Spot.php
|
|
||||||
$bResult = ($oFeed->addManualPosition($sLat, $sLng, $iTimestamp) > 0);
|
|
||||||
|
|
||||||
return self::getJsonResult($bResult, $bResult?'':$this->oDb->getLastError());
|
|
||||||
=======
|
|
||||||
$bSuccess = ($oFeed->addManualPosition($sLat, $sLng, $iTimestamp) > 0);
|
$bSuccess = ($oFeed->addManualPosition($sLat, $sLng, $iTimestamp) > 0);
|
||||||
|
|
||||||
if($bSuccess) {
|
if($bSuccess) {
|
||||||
@@ -678,7 +675,6 @@ class Spot extends Main
|
|||||||
else $sDesc = 'error.commit_db';
|
else $sDesc = 'error.commit_db';
|
||||||
|
|
||||||
return self::getJsonResult($bSuccess, $sDesc);
|
return self::getJsonResult($bSuccess, $sDesc);
|
||||||
>>>>>>> vue:lib/Spot.php
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAdminSettings($sType='') {
|
public function getAdminSettings($sType='') {
|
||||||
@@ -824,11 +820,7 @@ class Spot extends Main
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function buildGeoJSON($sCodeName) {
|
public function buildGeoJSON($sCodeName) {
|
||||||
return Converter::convertToGeoJson($sCodeName);
|
return Converter::convertToGeoJson($sCodeName)['logs'];
|
||||||
}
|
|
||||||
|
|
||||||
public function buildGeoJSON($sCodeName) {
|
|
||||||
return Converter::convertToGeoJson($sCodeName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function decToDms($dValue, $sType) {
|
public static function decToDms($dValue, $sType) {
|
||||||
|
|||||||
@@ -6,19 +6,10 @@ use Franzz\Objects\Translator;
|
|||||||
|
|
||||||
class Uploader extends UploadHandler
|
class Uploader extends UploadHandler
|
||||||
{
|
{
|
||||||
/**
|
private Media $oMedia;
|
||||||
* Medias Management
|
private Translator $oLang;
|
||||||
* @var Media
|
|
||||||
*/
|
|
||||||
private $oMedia;
|
|
||||||
|
|
||||||
/**
|
public string $sBody;
|
||||||
* Languages
|
|
||||||
* @var Translator
|
|
||||||
*/
|
|
||||||
private $oLang;
|
|
||||||
|
|
||||||
public $sBody;
|
|
||||||
|
|
||||||
function __construct(Media &$oMedia, Translator &$oLang)
|
function __construct(Media &$oMedia, Translator &$oLang)
|
||||||
{
|
{
|
||||||
@@ -27,7 +18,7 @@ class Uploader extends UploadHandler
|
|||||||
$this->sBody = '';
|
$this->sBody = '';
|
||||||
|
|
||||||
parent::__construct(array(
|
parent::__construct(array(
|
||||||
'upload_dir' => Media::MEDIA_FOLDER,
|
'upload_dir' => Media::MEDIA_FOLDER.'/',
|
||||||
'image_versions' => array(),
|
'image_versions' => array(),
|
||||||
'accept_file_types' => '/\.(gif|jpe?g|png|mov|mp4)$/i'
|
'accept_file_types' => '/\.(gif|jpe?g|png|mov|mp4)$/i'
|
||||||
));
|
));
|
||||||
@@ -46,12 +37,15 @@ class Uploader extends UploadHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function handle_file_upload($uploaded_file, $name, $size, $type, $error, $index = null, $content_range = null) {
|
protected function handle_file_upload($uploaded_file, $name, $size, $type, $error, $index = null, $content_range = null) {
|
||||||
$file = parent::handle_file_upload($uploaded_file, $name, $size, $type, $error, $index, $content_range);
|
$sExt = strtolower(pathinfo((string) $name, PATHINFO_EXTENSION));
|
||||||
|
$sStoredName = bin2hex(random_bytes(16)).($sExt !== ''?'.'.$sExt:'');
|
||||||
|
$file = parent::handle_file_upload($uploaded_file, $sStoredName, $size, $type, $error, $index, $content_range);
|
||||||
|
|
||||||
if(empty($file->error)) {
|
if(empty($file->error)) {
|
||||||
$asResult = $this->oMedia->addMedia($file->name);
|
$asResult = $this->oMedia->addMedia($file->name);
|
||||||
if(!$asResult['result']) $file->error = $this->get_error_message($asResult['desc'], $asResult['data']);
|
if(!$asResult['result']) $file->error = $this->get_error_message($asResult['desc'], $asResult['data']);
|
||||||
else {
|
else {
|
||||||
|
$file->original_name = basename((string) $name);
|
||||||
$file->id = $this->oMedia->getMediaId();
|
$file->id = $this->oMedia->getMediaId();
|
||||||
$file->thumbnail = $asResult['data']['thumb_path'];
|
$file->thumbnail = $asResult['data']['thumb_path'];
|
||||||
}
|
}
|
||||||
|
|||||||
122
lib/index.php
@@ -1,122 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/* Requests Handler */
|
|
||||||
|
|
||||||
//Start buffering
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
//Run from /dist/
|
|
||||||
$oLoader = require __DIR__.'/../vendor/autoload.php';
|
|
||||||
|
|
||||||
use Franzz\Objects\ToolBox;
|
|
||||||
use Franzz\Objects\Main;
|
|
||||||
use Franzz\Spot\Spot;
|
|
||||||
use Franzz\Spot\User;
|
|
||||||
|
|
||||||
ToolBox::fixGlobalVars($argv ?? array());
|
|
||||||
|
|
||||||
//Available variables
|
|
||||||
$sAction = $_REQUEST['a'] ?? '';
|
|
||||||
$sTimezone = $_REQUEST['t'] ?? '';
|
|
||||||
$sName = $_GET['name'] ?? '';
|
|
||||||
$sContent = $_GET['content'] ?? '';
|
|
||||||
$iProjectId = $_REQUEST['id_project'] ?? 0 ;
|
|
||||||
$sField = $_REQUEST['field'] ?? '';
|
|
||||||
$oValue = $_REQUEST['value'] ?? '';
|
|
||||||
$iId = $_REQUEST['id'] ?? 0 ;
|
|
||||||
$sType = $_REQUEST['type'] ?? '';
|
|
||||||
$sEmail = $_REQUEST['email'] ?? '';
|
|
||||||
$sLat = $_REQUEST['latitude'] ?? '';
|
|
||||||
$sLng = $_REQUEST['longitude'] ?? '';
|
|
||||||
$iTimestamp = $_REQUEST['timestamp'] ?? 0;
|
|
||||||
|
|
||||||
//Initiate class
|
|
||||||
$oSpot = new Spot(__FILE__, $sTimezone);
|
|
||||||
$oSpot->setProjectId($iProjectId);
|
|
||||||
|
|
||||||
$sResult = '';
|
|
||||||
if($sAction!='')
|
|
||||||
{
|
|
||||||
switch($sAction)
|
|
||||||
{
|
|
||||||
case 'markers':
|
|
||||||
$sResult = $oSpot->getMarkers();
|
|
||||||
break;
|
|
||||||
case 'geojson':
|
|
||||||
$sResult = $oSpot->getProjectGeoJson();
|
|
||||||
break;
|
|
||||||
case 'next_feed':
|
|
||||||
$sResult = $oSpot->getNextFeed($iId);
|
|
||||||
break;
|
|
||||||
case 'new_feed':
|
|
||||||
$sResult = $oSpot->getNewFeed($iId);
|
|
||||||
break;
|
|
||||||
case 'add_post':
|
|
||||||
$sResult = $oSpot->addPost($sName, $sContent);
|
|
||||||
break;
|
|
||||||
case 'subscribe':
|
|
||||||
$sResult = $oSpot->subscribe($sEmail, $sName);
|
|
||||||
break;
|
|
||||||
case 'unsubscribe':
|
|
||||||
$sResult = $oSpot->unsubscribe();
|
|
||||||
break;
|
|
||||||
case 'unsubscribe_email':
|
|
||||||
$sResult = $oSpot->unsubscribeFromEmail($iId);
|
|
||||||
break;
|
|
||||||
case 'update_project':
|
|
||||||
$sResult = $oSpot->updateProject();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if($oSpot->checkUserClearance(User::CLEARANCE_ADMIN))
|
|
||||||
{
|
|
||||||
switch($sAction)
|
|
||||||
{
|
|
||||||
case 'upload':
|
|
||||||
$sResult = $oSpot->upload();
|
|
||||||
break;
|
|
||||||
case 'add_comment':
|
|
||||||
$sResult = $oSpot->addComment($iId, $sContent);
|
|
||||||
break;
|
|
||||||
case 'add_position':
|
|
||||||
$sResult = $oSpot->addPosition($sLat, $sLng, $iTimestamp);
|
|
||||||
<<<<<<< HEAD:index.php
|
|
||||||
break;
|
|
||||||
case 'admin_new':
|
|
||||||
$sResult = $oSpot->createProject();
|
|
||||||
=======
|
|
||||||
>>>>>>> vue:lib/index.php
|
|
||||||
break;
|
|
||||||
case 'admin_get':
|
|
||||||
$sResult = $oSpot->getAdminSettings();
|
|
||||||
break;
|
|
||||||
case 'admin_set':
|
|
||||||
$sResult = $oSpot->setAdminSettings($sType, $iId, $sField, $oValue);
|
|
||||||
break;
|
|
||||||
case 'admin_create':
|
|
||||||
$sResult = $oSpot->createAdminSettings($sType);
|
|
||||||
break;
|
|
||||||
case 'admin_delete':
|
|
||||||
$sResult = $oSpot->deleteAdminSettings($sType, $iId);
|
|
||||||
break;
|
|
||||||
case 'generate_cron':
|
|
||||||
$sResult = $oSpot->genCronFile();
|
|
||||||
break;
|
|
||||||
case 'sql':
|
|
||||||
$sResult = $oSpot->getDbBuildScript();
|
|
||||||
break;
|
|
||||||
case 'build_geojson':
|
|
||||||
$sResult = $oSpot->buildGeoJSON($sName);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$sResult = Main::getJsonResult(false, Main::NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else $sResult = Main::getJsonResult(false, Main::NOT_FOUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else $sResult = $oSpot->getAppMainPage();
|
|
||||||
|
|
||||||
$sDebug = ob_get_clean();
|
|
||||||
if(Settings::DEBUG && $sDebug!='') $oSpot->addUncaughtError($sDebug);
|
|
||||||
|
|
||||||
echo $sResult;
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
|
|
||||||
<title>[#]lang:email.confirmation.subject[#]</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span style="color: transparent; display: none !important; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">[#]lang:email.confirmation.preheader[#]</span>
|
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="width:100%;max-width:600px;">
|
|
||||||
<tr>
|
|
||||||
<td width="20%"><img src="[#]local_server[#]images/icons/mstile-144x144.png" width="90%" border="0" alt="logo" /></td>
|
|
||||||
<td><h1>[#]lang:email.confirmation.thanks_subject[#]</h1></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">
|
|
||||||
<p align="justify">[#]lang:email.confirmation.body_1[#]</p>
|
|
||||||
<p align="justify">[#]lang:email.confirmation.body_2[#]</p>
|
|
||||||
<p align="justify">[#]lang:email.confirmation.body_3[#]</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">
|
|
||||||
<p>[#]lang:email.confirmation.conclusion[#]<br />[#]lang:email.confirmation.signature[#]</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">
|
|
||||||
<p>[#]lang:email.unsubscribe[#] <a href="[#]unsubscribe_link[#]" target="_blank" rel="noopener">[#]lang:email.unsubscribe_button[#]</a></p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
2183
package-lock.json
generated
11
package.json
@@ -10,12 +10,12 @@
|
|||||||
},
|
},
|
||||||
"name": "spot",
|
"name": "spot",
|
||||||
"description": "FindMeSpot & GPX integration",
|
"description": "FindMeSpot & GPX integration",
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "webpack --config build/webpack.config.js --mode development",
|
"dev": "flock -n node_modules/.webpack-build.lock webpack --config build/webpack.config.js --mode development",
|
||||||
"prod": "webpack --config build/webpack.config.js --mode production"
|
"prod": "flock -n node_modules/.webpack-build.lock webpack --config build/webpack.config.js --mode production"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Franzz",
|
"author": "Franzz",
|
||||||
@@ -26,13 +26,10 @@
|
|||||||
"@uppy/core": "^5.2.0",
|
"@uppy/core": "^5.2.0",
|
||||||
"@uppy/xhr-upload": "^5.2.0",
|
"@uppy/xhr-upload": "^5.2.0",
|
||||||
"autosize": "^6.0.1",
|
"autosize": "^6.0.1",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
|
||||||
"copy-webpack-plugin": "^14.0.0",
|
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"html-loader": "^5.0.0",
|
|
||||||
"maplibre-gl": "^5.4.0",
|
"maplibre-gl": "^5.4.0",
|
||||||
"sass": "^1.97.2",
|
"sass": "^1.97.2",
|
||||||
"sass-loader": "^16.0.5",
|
"sass-loader": "^17.0.0",
|
||||||
"simplebar-vue": "^2.3.3",
|
"simplebar-vue": "^2.3.3",
|
||||||
"vue": "^3.3.8",
|
"vue": "^3.3.8",
|
||||||
"vue-style-loader": "^4.1.3"
|
"vue-style-loader": "^4.1.3"
|
||||||
|
|||||||
7
public/index.php
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
|
use Franzz\Spot\Controller;
|
||||||
|
|
||||||
|
echo (new Controller())->handle(__FILE__, $argv ?? array());
|
||||||
38
readme.md
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
* npm 18+
|
* npm 24+
|
||||||
* composer
|
* composer
|
||||||
* php-mbstring
|
* php-mbstring
|
||||||
* php-imagick
|
* php-imagick
|
||||||
@@ -25,28 +25,32 @@
|
|||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
1. Clone Git onto web server
|
1. Clone Git onto web server
|
||||||
<<<<<<< HEAD
|
2. Update php.ini parameters
|
||||||
2. Install dependencies & update php.ini parameters
|
3. Copy timezone data: mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb -u root mysql
|
||||||
3. Copy timezone data: mariadb_tzinfo_to_sql /usr/share/zoneinfo | mariadb -u root mysql
|
|
||||||
4. Copy settings-sample.php to settings.php and populate
|
4. Copy settings-sample.php to settings.php and populate
|
||||||
5. Go to #admin and create a new project, feed & maps
|
5. Follow CI/CD script in .gitea/workflows/deploy.yml
|
||||||
6. Add a GPX file named <project_codename>.gpx to /geo/
|
|
||||||
7. Run composer install
|
|
||||||
=======
|
|
||||||
2. composer install
|
|
||||||
3. npm install webpack
|
|
||||||
4. npm run dev
|
|
||||||
5. Update php.ini parameters
|
|
||||||
6. Copy timezone data: mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb -u root mysql
|
|
||||||
7. Copy settings-sample.php to settings.php and populate
|
|
||||||
8. Go to #admin and create a new project, feed & maps
|
8. Go to #admin and create a new project, feed & maps
|
||||||
9. Add a GPX file named <project_codename>.gpx to /geo/
|
9. Add a GPX file named <project_codename>.gpx to /resources/geo/
|
||||||
|
|
||||||
|
## Web Root
|
||||||
|
|
||||||
|
The web server should serve `public/` as the application document root. PHP source, configuration, Composer dependencies, uploaded files, and GPX data stay outside the public tree; `public/index.php` is the front controller and webpack writes generated frontend assets to `public/assets/`.
|
||||||
|
|
||||||
|
Runtime data is exposed through symlinks only: `public/files -> ../files` and `public/geo -> ../resources/geo`. The build must not copy uploaded media or GPX data into `public/`.
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
When developing Spot and the sibling `objects` library together, install dependencies through the local Composer manifest:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
COMPOSER=composer.dev.json composer update
|
||||||
|
```
|
||||||
|
|
||||||
|
This makes Composer link `vendor/franzz/objects` to `../objects` and autoload that namespace directly from the local source path. Production continues to use `composer.json`, which installs `franzz/objects` from its Git repository. Commit and publish `objects` changes before updating/deploying a Spot version that relies on them.
|
||||||
|
|
||||||
>>>>>>> vue
|
|
||||||
## To Do List
|
## To Do List
|
||||||
|
|
||||||
* Add mail frequency slider
|
* Add mail frequency slider
|
||||||
* Use WMTS servers directly when not using Geo Caching Server
|
* Use WMTS servers directly when not using Geo Caching Server
|
||||||
* Allow HEIF picture format
|
* Allow HEIF picture format
|
||||||
* Fix .MOV playback on windows firefox
|
|
||||||
* Garmin InReach Integration
|
* Garmin InReach Integration
|
||||||
|
|||||||
215376
resources/geo/gr20.gpx
Normal file
@@ -284,8 +284,7 @@
|
|||||||
<ele>792</ele>
|
<ele>792</ele>
|
||||||
<time>2019-03-16T19:58:28.000Z</time>
|
<time>2019-03-16T19:58:28.000Z</time>
|
||||||
<name>Abri des Couloumates</name>
|
<name>Abri des Couloumates</name>
|
||||||
<desc><div>
|
<desc>8 places, table, cheminée, eau</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">8 places, table, cheminée, eau</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>ea4716b92363404d90272b73c50e2a23</ql:key>
|
<ql:key>ea4716b92363404d90272b73c50e2a23</ql:key>
|
||||||
@@ -333,8 +332,7 @@
|
|||||||
<ele>0</ele>
|
<ele>0</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Albergue Aysa</name>
|
<name>Albergue Aysa</name>
|
||||||
<desc><div>
|
<desc>http://www.albergueaysa.com/tarifas-58/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.albergueaysa.com/tarifas-58/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>effcbb203a05237e501feab97e95e467</ql:key>
|
<ql:key>effcbb203a05237e501feab97e95e467</ql:key>
|
||||||
@@ -359,8 +357,7 @@
|
|||||||
<ele>1518</ele>
|
<ele>1518</ele>
|
||||||
<time>2019-01-03T13:46:55.000Z</time>
|
<time>2019-01-03T13:46:55.000Z</time>
|
||||||
<name>Auberge de la Munia</name>
|
<name>Auberge de la Munia</name>
|
||||||
<desc><div>
|
<desc>http://www.aubergedelamunia.com</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.aubergedelamunia.com</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>c361f2d3b06b552daea31885fb04fb57</ql:key>
|
<ql:key>c361f2d3b06b552daea31885fb04fb57</ql:key>
|
||||||
@@ -385,8 +382,7 @@
|
|||||||
<ele>1825</ele>
|
<ele>1825</ele>
|
||||||
<time>2019-03-05T17:41:02.000Z</time>
|
<time>2019-03-05T17:41:02.000Z</time>
|
||||||
<name>Auberge Le Maillet</name>
|
<name>Auberge Le Maillet</name>
|
||||||
<desc><div>
|
<desc>https://www.facebook.com/aubergemaillet/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://www.facebook.com/aubergemaillet/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>de4797e810d8eacfe065187f98ee4b4f</ql:key>
|
<ql:key>de4797e810d8eacfe065187f98ee4b4f</ql:key>
|
||||||
@@ -463,8 +459,7 @@
|
|||||||
<ele>2796</ele>
|
<ele>2796</ele>
|
||||||
<time>2019-01-01T17:45:16.000Z</time>
|
<time>2019-01-01T17:45:16.000Z</time>
|
||||||
<name>Brèche de Roland</name>
|
<name>Brèche de Roland</name>
|
||||||
<desc><div>
|
<desc>2804m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2804m</p></div></desc>
|
|
||||||
<sym>Valley</sym>
|
<sym>Valley</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>100762362bf6955ec53b1a19efb5453e</ql:key>
|
<ql:key>100762362bf6955ec53b1a19efb5453e</ql:key>
|
||||||
@@ -566,10 +561,9 @@ This is waypoint no: 467</desc>
|
|||||||
<wpt lat="42.50383704" lon="2.44530734">
|
<wpt lat="42.50383704" lon="2.44530734">
|
||||||
<time>2010-06-09T19:58:08.000Z</time>
|
<time>2010-06-09T19:58:08.000Z</time>
|
||||||
<name>Cabane Arago</name>
|
<name>Cabane Arago</name>
|
||||||
<desc><div>
|
<desc>Cabane en bon état avec point d'eau.</p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Cabane en bon état avec point d'eau.</p>
|
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Détails : http://www.pyrenees-refuges.com/fr/affiche.php?numenr=19#15/42.5039/2.4449</p></div></desc>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Détails : http://www.pyrenees-refuges.com/fr/affiche.php?numenr=19#15/42.5039/2.4449</desc>
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>fb82be0d23628bbbf4a9a0d7f7ec00a7</ql:key>
|
<ql:key>fb82be0d23628bbbf4a9a0d7f7ec00a7</ql:key>
|
||||||
@@ -641,10 +635,9 @@ This is waypoint no: 467</desc>
|
|||||||
<ele>0</ele>
|
<ele>0</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Cabane d'Azpegi</name>
|
<name>Cabane d'Azpegi</name>
|
||||||
<desc><div>
|
<desc>Eau dispo.</p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Eau dispo.</p>
|
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.pyrenees-refuges.com/fr/affiche.php?numenr=1234#15/43.0323/-1.2273</p></div></desc>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.pyrenees-refuges.com/fr/affiche.php?numenr=1234#15/43.0323/-1.2273</desc>
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>5de226709a81feef249d27923449f91d</ql:key>
|
<ql:key>5de226709a81feef249d27923449f91d</ql:key>
|
||||||
@@ -691,8 +684,7 @@ This is waypoint no: 467</desc>
|
|||||||
<wpt lat="42.74526001" lon="-0.09167068">
|
<wpt lat="42.74526001" lon="-0.09167068">
|
||||||
<time>2010-06-07T19:05:10.000Z</time>
|
<time>2010-06-07T19:05:10.000Z</time>
|
||||||
<name>Cabane de Lourdes</name>
|
<name>Cabane de Lourdes</name>
|
||||||
<desc><div>
|
<desc>http://www.pyrenees-refuges.com/fr/affiche.php?numenr=210#15/42.7470/-0.0921</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.pyrenees-refuges.com/fr/affiche.php?numenr=210#15/42.7470/-0.0921</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>c1e7cc7d74df5fc6433223182919e90f</ql:key>
|
<ql:key>c1e7cc7d74df5fc6433223182919e90f</ql:key>
|
||||||
@@ -831,12 +823,11 @@ This is waypoint no: 467</desc>
|
|||||||
<wpt lat="42.75625706" lon="0.11215925">
|
<wpt lat="42.75625706" lon="0.11215925">
|
||||||
<time>2010-06-07T20:45:44.000Z</time>
|
<time>2010-06-07T20:45:44.000Z</time>
|
||||||
<name>Cabane Des Aguilous</name>
|
<name>Cabane Des Aguilous</name>
|
||||||
<desc><div>
|
<desc>Eau disponible.</p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Eau disponible.</p>
|
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A priori privé.</p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A priori privé.</p>
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.pyrenees-refuges.com/fr/affiche.php?numenr=739</p></div></desc>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.pyrenees-refuges.com/fr/affiche.php?numenr=739</desc>
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>7db2219ea46a4d9b659790d6f2866d61</ql:key>
|
<ql:key>7db2219ea46a4d9b659790d6f2866d61</ql:key>
|
||||||
@@ -1183,8 +1174,7 @@ This is waypoint no: 122</desc>
|
|||||||
<wpt lat="42.93983283" lon="-0.69825623">
|
<wpt lat="42.93983283" lon="-0.69825623">
|
||||||
<time>2010-06-06T08:31:13.000Z</time>
|
<time>2010-06-06T08:31:13.000Z</time>
|
||||||
<name>Cayolars d'Anaye (cabane)</name>
|
<name>Cayolars d'Anaye (cabane)</name>
|
||||||
<desc><div>
|
<desc>http://www.pyrenees-refuges.com/fr/affiche.php?numenr=8#15/42.9395/-0.6982</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.pyrenees-refuges.com/fr/affiche.php?numenr=8#15/42.9395/-0.6982</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>f26c7a98925caa71568322af33513a25</ql:key>
|
<ql:key>f26c7a98925caa71568322af33513a25</ql:key>
|
||||||
@@ -1209,10 +1199,9 @@ This is waypoint no: 122</desc>
|
|||||||
<ele>943</ele>
|
<ele>943</ele>
|
||||||
<time>2019-01-01T15:42:56.000Z</time>
|
<time>2019-01-01T15:42:56.000Z</time>
|
||||||
<name>Chalet de l'Albère</name>
|
<name>Chalet de l'Albère</name>
|
||||||
<desc><div>
|
<desc>http://lalbere.net/quelsServices/Chalet.htm</p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://lalbere.net/quelsServices/Chalet.htm</p>
|
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2 dortoirs de 10 et 14 couchages</p></div></desc>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2 dortoirs de 10 et 14 couchages</desc>
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>cb03db82142bc5eb5453e8bfa21575ca</ql:key>
|
<ql:key>cb03db82142bc5eb5453e8bfa21575ca</ql:key>
|
||||||
@@ -1283,8 +1272,7 @@ This is waypoint no: 122</desc>
|
|||||||
<ele>1318</ele>
|
<ele>1318</ele>
|
||||||
<time>2019-01-03T14:45:51.000Z</time>
|
<time>2019-01-03T14:45:51.000Z</time>
|
||||||
<name>Chalets d'Iraty</name>
|
<name>Chalets d'Iraty</name>
|
||||||
<desc><div>
|
<desc>Gite 13 places disponibles. Voir en bas de la page http://www.chalets-iraty.com/preparez-votre-sejour/les-hebergements/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Gite 13 places disponibles. Voir en bas de la page http://www.chalets-iraty.com/preparez-votre-sejour/les-hebergements/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>7fb5f721f009d4e159eef2731ade0241</ql:key>
|
<ql:key>7fb5f721f009d4e159eef2731ade0241</ql:key>
|
||||||
@@ -3058,8 +3046,7 @@ This is waypoint no: 13</desc>
|
|||||||
<wpt lat="42.43853331" lon="2.71061897">
|
<wpt lat="42.43853331" lon="2.71061897">
|
||||||
<time>2010-06-09T21:16:12.000Z</time>
|
<time>2010-06-09T21:16:12.000Z</time>
|
||||||
<name>Coll Del Ric</name>
|
<name>Coll Del Ric</name>
|
||||||
<desc><div>
|
<desc>958m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">958m</p></div></desc>
|
|
||||||
<sym>Valley</sym>
|
<sym>Valley</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>9fd7b75d96f77439315cdcaeaa7d71c1</ql:key>
|
<ql:key>9fd7b75d96f77439315cdcaeaa7d71c1</ql:key>
|
||||||
@@ -5567,8 +5554,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>1445</ele>
|
<ele>1445</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Gîte d'étape l'Hospitalité</name>
|
<name>Gîte d'étape l'Hospitalité</name>
|
||||||
<desc><div>
|
<desc>https://www.gitelhospitalite.com/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://www.gitelhospitalite.com/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>ed62023e3282c3f95c2d4f281b160e25</ql:key>
|
<ql:key>ed62023e3282c3f95c2d4f281b160e25</ql:key>
|
||||||
@@ -5641,8 +5627,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>1354</ele>
|
<ele>1354</ele>
|
||||||
<time>2019-03-03T18:38:42.000Z</time>
|
<time>2019-03-03T18:38:42.000Z</time>
|
||||||
<name>Gîte Le Gypaète</name>
|
<name>Gîte Le Gypaète</name>
|
||||||
<desc><div>
|
<desc>http://legypaete.pagesperso-orange.fr/france/tarifs.htm</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://legypaete.pagesperso-orange.fr/france/tarifs.htm</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>019e5dbc025c17ecc78d89e22e6e7ea7</ql:key>
|
<ql:key>019e5dbc025c17ecc78d89e22e6e7ea7</ql:key>
|
||||||
@@ -5845,8 +5830,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>200</ele>
|
<ele>200</ele>
|
||||||
<time>2019-03-24T19:08:50.000Z</time>
|
<time>2019-03-24T19:08:50.000Z</time>
|
||||||
<name>Hostal Elizondo</name>
|
<name>Hostal Elizondo</name>
|
||||||
<desc><div>
|
<desc>https://hostalelizondo.com/tarifas-reservas/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://hostalelizondo.com/tarifas-reservas/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>f6d87056620938b3dd61d035878e6371</ql:key>
|
<ql:key>f6d87056620938b3dd61d035878e6371</ql:key>
|
||||||
@@ -5871,8 +5855,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>1117</ele>
|
<ele>1117</ele>
|
||||||
<time>2019-03-05T18:14:03.000Z</time>
|
<time>2019-03-05T18:14:03.000Z</time>
|
||||||
<name>Hostal La Fuen</name>
|
<name>Hostal La Fuen</name>
|
||||||
<desc><div>
|
<desc>http://www.lafuen.com/tarifas</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.lafuen.com/tarifas</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>b4ccd9a66257d871f35c38476d17a037</ql:key>
|
<ql:key>b4ccd9a66257d871f35c38476d17a037</ql:key>
|
||||||
@@ -5897,8 +5880,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>950</ele>
|
<ele>950</ele>
|
||||||
<time>2019-02-19T19:26:12.000Z</time>
|
<time>2019-02-19T19:26:12.000Z</time>
|
||||||
<name>Hostel Roncesvalles - Orreaga</name>
|
<name>Hostel Roncesvalles - Orreaga</name>
|
||||||
<desc><div>
|
<desc>http://www.alberguederoncesvalles.com</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.alberguederoncesvalles.com</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>4ff045dfbbcfd8b2b377cda457f9b0f9</ql:key>
|
<ql:key>4ff045dfbbcfd8b2b377cda457f9b0f9</ql:key>
|
||||||
@@ -5969,8 +5951,7 @@ This is waypoint no: 450</desc>
|
|||||||
<wpt lat="42.47334936" lon="2.66873621">
|
<wpt lat="42.47334936" lon="2.66873621">
|
||||||
<time>2019-04-07T08:01:57.000Z</time>
|
<time>2019-04-07T08:01:57.000Z</time>
|
||||||
<name>Hotel de France</name>
|
<name>Hotel de France</name>
|
||||||
<desc><div>
|
<desc>http://www.h.df66110.com/#Tarifs.D</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.h.df66110.com/#Tarifs.D</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>5759f6f2f48a33e959eac31d26ae7c43</ql:key>
|
<ql:key>5759f6f2f48a33e959eac31d26ae7c43</ql:key>
|
||||||
@@ -6214,8 +6195,7 @@ This is waypoint no: 450</desc>
|
|||||||
<wpt lat="42.95145600" lon="-0.89058500">
|
<wpt lat="42.95145600" lon="-0.89058500">
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Lakartxela</name>
|
<name>Lakartxela</name>
|
||||||
<desc><div>
|
<desc>1979m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1979m</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>a2006c92f1943060d1033d2d55556c02</ql:key>
|
<ql:key>a2006c92f1943060d1033d2d55556c02</ql:key>
|
||||||
@@ -6287,8 +6267,7 @@ This is waypoint no: 450</desc>
|
|||||||
<wpt lat="42.51794245" lon="2.52444886">
|
<wpt lat="42.51794245" lon="2.52444886">
|
||||||
<time>2010-06-09T20:21:54.000Z</time>
|
<time>2010-06-09T20:21:54.000Z</time>
|
||||||
<name>Maison Forestiere l'estanyol</name>
|
<name>Maison Forestiere l'estanyol</name>
|
||||||
<desc><div>
|
<desc>Cabane non gardée</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Cabane non gardée</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>81af5476ea38b90473fa5eda89027bed</ql:key>
|
<ql:key>81af5476ea38b90473fa5eda89027bed</ql:key>
|
||||||
@@ -6313,8 +6292,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>3331</ele>
|
<ele>3331</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Monte Perdido</name>
|
<name>Monte Perdido</name>
|
||||||
<desc><div>
|
<desc>3348m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">3348m</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>7609003845b52fc6dd2ad0c7b018a691</ql:key>
|
<ql:key>7609003845b52fc6dd2ad0c7b018a691</ql:key>
|
||||||
@@ -6339,8 +6317,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>1847</ele>
|
<ele>1847</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Mujer Muerta</name>
|
<name>Mujer Muerta</name>
|
||||||
<desc><div>
|
<desc>1859m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1859m</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>fe68b3f5bfaed7d61097710532b22a5a</ql:key>
|
<ql:key>fe68b3f5bfaed7d61097710532b22a5a</ql:key>
|
||||||
@@ -6906,8 +6883,7 @@ This is waypoint no: 450</desc>
|
|||||||
<wpt lat="42.84374800" lon="-0.43785900">
|
<wpt lat="42.84374800" lon="-0.43785900">
|
||||||
<time>2018-12-23T14:20:42.000Z</time>
|
<time>2018-12-23T14:20:42.000Z</time>
|
||||||
<name>Pic du Midi d'Ossau</name>
|
<name>Pic du Midi d'Ossau</name>
|
||||||
<desc><div>
|
<desc>15/08/19</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">15/08/19</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>fd028610b69593330af80c2cfa0b6606</ql:key>
|
<ql:key>fd028610b69593330af80c2cfa0b6606</ql:key>
|
||||||
@@ -6931,8 +6907,7 @@ This is waypoint no: 450</desc>
|
|||||||
<wpt lat="42.48251726" lon="2.94813889">
|
<wpt lat="42.48251726" lon="2.94813889">
|
||||||
<time>2010-06-09T21:42:53.000Z</time>
|
<time>2010-06-09T21:42:53.000Z</time>
|
||||||
<name>Pic Neulos</name>
|
<name>Pic Neulos</name>
|
||||||
<desc><div>
|
<desc>1259m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1259m</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>7ebcf9fe04b0b0dbf2311c90752a4274</ql:key>
|
<ql:key>7ebcf9fe04b0b0dbf2311c90752a4274</ql:key>
|
||||||
@@ -6957,8 +6932,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>3388</ele>
|
<ele>3388</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Pico Aneto</name>
|
<name>Pico Aneto</name>
|
||||||
<desc><div>
|
<desc>3404m </desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">3404m </p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>3cabab68c61ea3fe78792d8c98da1f97</ql:key>
|
<ql:key>3cabab68c61ea3fe78792d8c98da1f97</ql:key>
|
||||||
@@ -7262,8 +7236,7 @@ This is waypoint no: 450</desc>
|
|||||||
<wpt lat="42.56985426" lon="1.93211317">
|
<wpt lat="42.56985426" lon="1.93211317">
|
||||||
<time>2010-06-09T18:43:54.000Z</time>
|
<time>2010-06-09T18:43:54.000Z</time>
|
||||||
<name>Puig Carlit</name>
|
<name>Puig Carlit</name>
|
||||||
<desc><div>
|
<desc>2921m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2921m</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>5327d145ed2ca1b7cec310f0e6083ed0</ql:key>
|
<ql:key>5327d145ed2ca1b7cec310f0e6083ed0</ql:key>
|
||||||
@@ -7287,8 +7260,7 @@ This is waypoint no: 450</desc>
|
|||||||
<wpt lat="42.47702837" lon="3.04010153">
|
<wpt lat="42.47702837" lon="3.04010153">
|
||||||
<time>2010-06-09T21:52:13.000Z</time>
|
<time>2010-06-09T21:52:13.000Z</time>
|
||||||
<name>Puig de Sallfort</name>
|
<name>Puig de Sallfort</name>
|
||||||
<desc><div>
|
<desc>978m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">978m</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>4545cf0ebd06ec5f34614926ce8e2fea</ql:key>
|
<ql:key>4545cf0ebd06ec5f34614926ce8e2fea</ql:key>
|
||||||
@@ -7312,8 +7284,7 @@ This is waypoint no: 450</desc>
|
|||||||
<wpt lat="42.47297287" lon="2.99832344">
|
<wpt lat="42.47297287" lon="2.99832344">
|
||||||
<time>2010-06-09T21:46:49.000Z</time>
|
<time>2010-06-09T21:46:49.000Z</time>
|
||||||
<name>Puig Dels Quatre Termes</name>
|
<name>Puig Dels Quatre Termes</name>
|
||||||
<desc><div>
|
<desc>1158m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1158m</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>c00ef5c9fc6312af27e421e91e941801</ql:key>
|
<ql:key>c00ef5c9fc6312af27e421e91e941801</ql:key>
|
||||||
@@ -7338,8 +7309,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>2751</ele>
|
<ele>2751</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Punta Escuzana</name>
|
<name>Punta Escuzana</name>
|
||||||
<desc><div>
|
<desc>2847m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2847m</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>865626bf14dc7fde70a26412219abcc5</ql:key>
|
<ql:key>865626bf14dc7fde70a26412219abcc5</ql:key>
|
||||||
@@ -7481,8 +7451,7 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>2452</ele>
|
<ele>2452</ele>
|
||||||
<time>2019-03-17T19:38:52.000Z</time>
|
<time>2019-03-17T19:38:52.000Z</time>
|
||||||
<name>Refuge de Baborte (cabane)</name>
|
<name>Refuge de Baborte (cabane)</name>
|
||||||
<desc><div>
|
<desc>9 places</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">9 places</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>9d283198bd5257de5cf19f3d7f98fd65</ql:key>
|
<ql:key>9d283198bd5257de5cf19f3d7f98fd65</ql:key>
|
||||||
@@ -7506,8 +7475,7 @@ This is waypoint no: 450</desc>
|
|||||||
<wpt lat="42.50199194" lon="2.55074222">
|
<wpt lat="42.50199194" lon="2.55074222">
|
||||||
<time>2010-06-09T21:01:14.000Z</time>
|
<time>2010-06-09T21:01:14.000Z</time>
|
||||||
<name>Refuge de Batère</name>
|
<name>Refuge de Batère</name>
|
||||||
<desc><div>
|
<desc>http://www.refugedebatere.fr/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.refugedebatere.fr/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>fd1a4f32290aaf4d1bc36410b694ad44</ql:key>
|
<ql:key>fd1a4f32290aaf4d1bc36410b694ad44</ql:key>
|
||||||
@@ -7579,9 +7547,8 @@ This is waypoint no: 450</desc>
|
|||||||
<ele>2215</ele>
|
<ele>2215</ele>
|
||||||
<time>2010-06-20T08:37:37.000Z</time>
|
<time>2010-06-20T08:37:37.000Z</time>
|
||||||
<name>Refuge de Cóms de Jan (cabane)</name>
|
<name>Refuge de Cóms de Jan (cabane)</name>
|
||||||
<desc><div>
|
<desc>6 à 8 places.</p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">6 à 8 places.</p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Eau</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Eau</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>696be584f34734f98a23161d302362c0</ql:key>
|
<ql:key>696be584f34734f98a23161d302362c0</ql:key>
|
||||||
@@ -7868,8 +7835,7 @@ This is waypoint no: 61</desc>
|
|||||||
<ele>1687</ele>
|
<ele>1687</ele>
|
||||||
<time>2010-06-09T19:55:51.000Z</time>
|
<time>2010-06-09T19:55:51.000Z</time>
|
||||||
<name>Refuge des Mariailles</name>
|
<name>Refuge des Mariailles</name>
|
||||||
<desc><div>
|
<desc>http://www.refugedemariailles.fr</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.refugedemariailles.fr</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>bdf8ba39360cd80335ced574be2bc6a3</ql:key>
|
<ql:key>bdf8ba39360cd80335ced574be2bc6a3</ql:key>
|
||||||
@@ -7922,8 +7888,7 @@ This is waypoint no: 61</desc>
|
|||||||
<ele>2231</ele>
|
<ele>2231</ele>
|
||||||
<time>2019-03-17T19:31:12.000Z</time>
|
<time>2019-03-17T19:31:12.000Z</time>
|
||||||
<name>Refuge du Pinet</name>
|
<name>Refuge du Pinet</name>
|
||||||
<desc><div>
|
<desc>https://refugeetangpinet.ffcam.fr/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://refugeetangpinet.ffcam.fr/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>429442ea51231ee38143d5b28a1479d7</ql:key>
|
<ql:key>429442ea51231ee38143d5b28a1479d7</ql:key>
|
||||||
@@ -8064,12 +8029,11 @@ This is waypoint no: 61</desc>
|
|||||||
<ele>0</ele>
|
<ele>0</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Refugi d'Alós</name>
|
<name>Refugi d'Alós</name>
|
||||||
<desc><div>
|
<desc>Refuge potentiellement ouvert: </p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge potentiellement ouvert: </p>
|
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://www.facebook.com/refugidalos/app/298669810188442/</p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://www.facebook.com/refugidalos/app/298669810188442/</p>
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">email : refugialos@gmail.com (à contacter en mai pour confimer)</p></div></desc>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">email : refugialos@gmail.com (à contacter en mai pour confimer)</desc>
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>a46888616221d4cc2a687fb81b2ffd3b</ql:key>
|
<ql:key>a46888616221d4cc2a687fb81b2ffd3b</ql:key>
|
||||||
@@ -8094,8 +8058,7 @@ This is waypoint no: 61</desc>
|
|||||||
<ele>2348</ele>
|
<ele>2348</ele>
|
||||||
<time>2019-03-22T19:27:22.000Z</time>
|
<time>2019-03-22T19:27:22.000Z</time>
|
||||||
<name>Refugi d'Armitges</name>
|
<name>Refugi d'Armitges</name>
|
||||||
<desc><div>
|
<desc>http://www.amitges.com/fr/refuge/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.amitges.com/fr/refuge/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>17a261d7af5168511bbd82d94c2cabc4</ql:key>
|
<ql:key>17a261d7af5168511bbd82d94c2cabc4</ql:key>
|
||||||
@@ -8146,10 +8109,9 @@ This is waypoint no: 191</desc>
|
|||||||
<ele>2232</ele>
|
<ele>2232</ele>
|
||||||
<time>2010-06-20T08:15:37.000Z</time>
|
<time>2010-06-20T08:15:37.000Z</time>
|
||||||
<name>Refugi de Certascan</name>
|
<name>Refugi de Certascan</name>
|
||||||
<desc><div>
|
<desc>http://www.certascan.com/muntanyes_de_llibertat/frances/refuge_certascan_pyrenees.htm</p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.certascan.com/muntanyes_de_llibertat/frances/refuge_certascan_pyrenees.htm</p>
|
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Picnic disponible.</p></div></desc>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Picnic disponible.</desc>
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>2c0c33260f385cc448334a7632533424</ql:key>
|
<ql:key>2c0c33260f385cc448334a7632533424</ql:key>
|
||||||
@@ -8174,8 +8136,7 @@ This is waypoint no: 191</desc>
|
|||||||
<ele>0</ele>
|
<ele>0</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Refugi de Conangles</name>
|
<name>Refugi de Conangles</name>
|
||||||
<desc><div>
|
<desc>https://eraportadaran.com/refugi-de-conangles/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://eraportadaran.com/refugi-de-conangles/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>d2803c148a3704682fd487e41bd7578f</ql:key>
|
<ql:key>d2803c148a3704682fd487e41bd7578f</ql:key>
|
||||||
@@ -8200,9 +8161,8 @@ This is waypoint no: 191</desc>
|
|||||||
<ele>0</ele>
|
<ele>0</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Refugi de Juclar</name>
|
<name>Refugi de Juclar</name>
|
||||||
<desc><div>
|
<desc>http://www.refugidejuclar.com/tarifes/</p>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.refugidejuclar.com/tarifes/</p>
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Demi pension + pic nic possible</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Demi pension + pic nic possible</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>a73c0358d64fc3e6b02227d49b00a0ec</ql:key>
|
<ql:key>a73c0358d64fc3e6b02227d49b00a0ec</ql:key>
|
||||||
@@ -8344,8 +8304,7 @@ This is waypoint no: 191</desc>
|
|||||||
<ele>2292</ele>
|
<ele>2292</ele>
|
||||||
<time>2010-06-18T21:08:53.000Z</time>
|
<time>2010-06-18T21:08:53.000Z</time>
|
||||||
<name>Refugi de Saboredo</name>
|
<name>Refugi de Saboredo</name>
|
||||||
<desc><div>
|
<desc>https://www.refusaboredo.com/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://www.refusaboredo.com/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>77f3140bde82a4405b0d9f3568129a00</ql:key>
|
<ql:key>77f3140bde82a4405b0d9f3568129a00</ql:key>
|
||||||
@@ -8370,8 +8329,7 @@ This is waypoint no: 191</desc>
|
|||||||
<ele>1969</ele>
|
<ele>1969</ele>
|
||||||
<time>2010-06-20T08:35:40.000Z</time>
|
<time>2010-06-20T08:35:40.000Z</time>
|
||||||
<name>Refugi de Sorteny</name>
|
<name>Refugi de Sorteny</name>
|
||||||
<desc><div>
|
<desc>https://www.refugisorteny.com/bienvenue-1/prix-et-conditions-1/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://www.refugisorteny.com/bienvenue-1/prix-et-conditions-1/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>203952dfb191452858e0ac99341e2373</ql:key>
|
<ql:key>203952dfb191452858e0ac99341e2373</ql:key>
|
||||||
@@ -8469,8 +8427,7 @@ This is waypoint no: 166</desc>
|
|||||||
<ele>1247</ele>
|
<ele>1247</ele>
|
||||||
<time>2019-03-22T19:09:35.000Z</time>
|
<time>2019-03-22T19:09:35.000Z</time>
|
||||||
<name>Refugi Rosta</name>
|
<name>Refugi Rosta</name>
|
||||||
<desc><div>
|
<desc>http://www.refugirosta.com/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.refugirosta.com/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>cf5b4f9675112d1ae475f9c12886c02a</ql:key>
|
<ql:key>cf5b4f9675112d1ae475f9c12886c02a</ql:key>
|
||||||
@@ -8495,8 +8452,7 @@ This is waypoint no: 166</desc>
|
|||||||
<ele>2209</ele>
|
<ele>2209</ele>
|
||||||
<time>2019-03-22T19:41:21.000Z</time>
|
<time>2019-03-22T19:41:21.000Z</time>
|
||||||
<name>Refugi Ventosa i Calvell</name>
|
<name>Refugi Ventosa i Calvell</name>
|
||||||
<desc><div>
|
<desc>http://www.refugiventosa.com/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.refugiventosa.com/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>e8de798e8af7954f7ced8b9d56f661ad</ql:key>
|
<ql:key>e8de798e8af7954f7ced8b9d56f661ad</ql:key>
|
||||||
@@ -8547,8 +8503,7 @@ This is waypoint no: 271</desc>
|
|||||||
<ele>1876</ele>
|
<ele>1876</ele>
|
||||||
<time>2010-06-17T22:17:23.000Z</time>
|
<time>2010-06-17T22:17:23.000Z</time>
|
||||||
<name>Refugio de Barrosa (cabane)</name>
|
<name>Refugio de Barrosa (cabane)</name>
|
||||||
<desc><div>
|
<desc>Eau disponible.</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Eau disponible.</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>ce1187ab5962b8095a1419455baf7ed7</ql:key>
|
<ql:key>ce1187ab5962b8095a1419455baf7ed7</ql:key>
|
||||||
@@ -8671,8 +8626,7 @@ This is waypoint no: 269</desc>
|
|||||||
<ele>2142</ele>
|
<ele>2142</ele>
|
||||||
<time>2019-07-23T14:21:36.000Z</time>
|
<time>2019-07-23T14:21:36.000Z</time>
|
||||||
<name>Refugio de la Renclusa</name>
|
<name>Refugio de la Renclusa</name>
|
||||||
<desc><div>
|
<desc>https://www.alberguesyrefugios.com/larenclusa/</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">https://www.alberguesyrefugios.com/larenclusa/</p></div></desc>
|
|
||||||
<sym>Residence</sym>
|
<sym>Residence</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>520291e75621d50f9dbf0d908421fe60</ql:key>
|
<ql:key>520291e75621d50f9dbf0d908421fe60</ql:key>
|
||||||
@@ -9754,8 +9708,7 @@ This is waypoint no: 425</desc>
|
|||||||
<ele>3278</ele>
|
<ele>3278</ele>
|
||||||
<time>2019-01-02T16:01:38.000Z</time>
|
<time>2019-01-02T16:01:38.000Z</time>
|
||||||
<name>Vignemale</name>
|
<name>Vignemale</name>
|
||||||
<desc><div>
|
<desc>3299m</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">3299m</p></div></desc>
|
|
||||||
<sym>Summit</sym>
|
<sym>Summit</sym>
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>9fe8fffc6c21e43dd3413d8ea4b14e63</ql:key>
|
<ql:key>9fe8fffc6c21e43dd3413d8ea4b14e63</ql:key>
|
||||||
@@ -9800,9 +9753,8 @@ This is waypoint no: 425</desc>
|
|||||||
</extensions>
|
</extensions>
|
||||||
</wpt>
|
</wpt>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 01</name>
|
<name>Etape 1</name>
|
||||||
<desc><div>
|
<desc>Banyuls-sur-Mer ➜ Chalet de l'Albère</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Banyuls-sur-Mer au Chalet de l'Albère</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>b6a67b2586a43bada478bd77cb97c25b</ql:key>
|
<ql:key>b6a67b2586a43bada478bd77cb97c25b</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -11358,9 +11310,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 02</name>
|
<name>Etape 2</name>
|
||||||
<desc><div>
|
<desc>Chalet de l'Albère ➜ Las Illas</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Chalet de l'Albère à Las Illas</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>5e4204b68d14b14d7b853e8b789f5f02</ql:key>
|
<ql:key>5e4204b68d14b14d7b853e8b789f5f02</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -13220,9 +13171,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 03</name>
|
<name>Etape 3</name>
|
||||||
<desc><div>
|
<desc>Las Illas ➜ Amélie-les-Bains</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Las Illas à Amélie-les-Bains</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>194d6fcabb2635a3e6d53ec725602fca</ql:key>
|
<ql:key>194d6fcabb2635a3e6d53ec725602fca</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -14450,9 +14400,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 04</name>
|
<name>Etape 4</name>
|
||||||
<desc><div>
|
<desc>Amélie-les-Bains ➜ Gîte de Batère</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Amélie-les-Bains au Gîte de Batère</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>42f893f700691772a5e1ecbeee786288</ql:key>
|
<ql:key>42f893f700691772a5e1ecbeee786288</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -15656,9 +15605,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 05</name>
|
<name>Etape 5</name>
|
||||||
<desc><div>
|
<desc>Gîte de Batère ➜ Refuge des Mariailles</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Gîte de Batère au Refuge des Mariailles</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>032c72bfc98ad3a5890aa08e6bd25179</ql:key>
|
<ql:key>032c72bfc98ad3a5890aa08e6bd25179</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -17944,9 +17892,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 06</name>
|
<name>Etape 6</name>
|
||||||
<desc><div>
|
<desc>Refuge des Mariailles ➜ Refuge d'Ull de Ter</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge des Mariailles au Refuge d'Ull de Ter</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>0afcd060b61c1b8c95632af4a86eb1ac</ql:key>
|
<ql:key>0afcd060b61c1b8c95632af4a86eb1ac</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -23574,9 +23521,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 07</name>
|
<name>Etape 7</name>
|
||||||
<desc><div>
|
<desc>Refuge d'Ull de Ter ➜ Gîte les Ramiers</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge d'Ull de Ter au Gîte les Ramiers</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>a1fc0e61ced1dff5dc3cae08940d5548</ql:key>
|
<ql:key>a1fc0e61ced1dff5dc3cae08940d5548</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -25230,9 +25176,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 08</name>
|
<name>Etape 8</name>
|
||||||
<desc><div>
|
<desc>Gîte les Ramiers ➜ Gîte d'étape l'Hospitalité</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Gîte les Ramiers au Gîte d'étape l'Hospitalité</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>a830d5354220dc21baf491062bb5f5b7</ql:key>
|
<ql:key>a830d5354220dc21baf491062bb5f5b7</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -28172,9 +28117,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 08 - HRP original</name>
|
<name>Etape 8 - HRP original</name>
|
||||||
<desc><div>
|
<desc>Gîte les Ramiers ➜ Gîte d'étape l'Hospitalité</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Gîte les Ramiers au Gîte d'étape l'Hospitalité</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>e4a405d8d6431425d26def48cfb8bdc2</ql:key>
|
<ql:key>e4a405d8d6431425d26def48cfb8bdc2</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -28442,9 +28386,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 09</name>
|
<name>Etape 9</name>
|
||||||
<desc><div>
|
<desc>Gîte d'étape l'Hospitalité ➜ Refuge de Sorteny</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Gîte d'étape l'Hospitalité au Refuge de Sorteny</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>6f8aefaa3cec6ee3deb5ebdba8cb78c2</ql:key>
|
<ql:key>6f8aefaa3cec6ee3deb5ebdba8cb78c2</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -39696,9 +39639,8 @@ This is waypoint no: 425</desc>
|
|||||||
</trkseg>
|
</trkseg>
|
||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 09 - HRP orginal</name>
|
<name>Etape 9 - HRP orginal</name>
|
||||||
<desc><div>
|
<desc>Gîte d'étape l'Hospitalité ➜ Refuge de Coms de Jan</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Gîte d'étape l'Hospitalité au Refuge de Coms de Jan</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>129ec58de2626772313bb5f5a69c57f0</ql:key>
|
<ql:key>129ec58de2626772313bb5f5a69c57f0</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -41063,8 +41005,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 10</name>
|
<name>Etape 10</name>
|
||||||
<desc><div>
|
<desc>Refuge de Sorteny ➜ Village de Marc</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge de Sorteny au Village de Marc</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>34c70c0bbb43e48a0aa292ec5f1988ea</ql:key>
|
<ql:key>34c70c0bbb43e48a0aa292ec5f1988ea</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -42495,8 +42436,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 11</name>
|
<name>Etape 11</name>
|
||||||
<desc><div>
|
<desc>Village de Marc ➜ Refugi de Certascan</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Village de Marc au Refugi de Certascan</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>dba07fa9a705cf9fdeb53d83d44209ea</ql:key>
|
<ql:key>dba07fa9a705cf9fdeb53d83d44209ea</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -43269,8 +43209,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 12</name>
|
<name>Etape 12</name>
|
||||||
<desc><div>
|
<desc>Refugi de Certascan ➜ Refuge del Fornet</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refugi de Certascan au Refuge del Fornet</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>d7e97a2f0d5dbfe670bd4f4e05ec25cc</ql:key>
|
<ql:key>d7e97a2f0d5dbfe670bd4f4e05ec25cc</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -49049,8 +48988,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 13</name>
|
<name>Etape 13</name>
|
||||||
<desc><div>
|
<desc>Refuge del Fornet ➜ Refuge de Colomèrs II</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge del Fornet au Refuge de Colomèrs II</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>dbb7d6c539233a18cab2ed8b6fd4f621</ql:key>
|
<ql:key>dbb7d6c539233a18cab2ed8b6fd4f621</ql:key>
|
||||||
<ql:flags>7</ql:flags>
|
<ql:flags>7</ql:flags>
|
||||||
@@ -57294,8 +57232,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 14</name>
|
<name>Etape 14</name>
|
||||||
<desc><div>
|
<desc>Refuge de Colomèrs II ➜ Refugi de Conangles</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge de Colomèrs II au Refugi de Conangles</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>27bc24fdad161d97804b9c42e190ca22</ql:key>
|
<ql:key>27bc24fdad161d97804b9c42e190ca22</ql:key>
|
||||||
<ql:flags>6</ql:flags>
|
<ql:flags>6</ql:flags>
|
||||||
@@ -58821,8 +58758,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 15</name>
|
<name>Etape 15</name>
|
||||||
<desc><div>
|
<desc>Refugi de Conangles ➜ Refugio de la Renclusa</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refugi de Conangles au Refugio de la Renclusa</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>5d011dacc7bf2927f3343e6f451467f9</ql:key>
|
<ql:key>5d011dacc7bf2927f3343e6f451467f9</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -64468,8 +64404,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 15 - Variante Aneto</name>
|
<name>Etape 15 - Variante Aneto</name>
|
||||||
<desc><div>
|
<desc>Ascension Aneto</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ascension Aneto</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>1e42af4594337d4fa78cb425c6349615</ql:key>
|
<ql:key>1e42af4594337d4fa78cb425c6349615</ql:key>
|
||||||
<ql:flags>6</ql:flags>
|
<ql:flags>6</ql:flags>
|
||||||
@@ -68958,8 +68893,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 16</name>
|
<name>Etape 16</name>
|
||||||
<desc><div>
|
<desc>Refugio de la Renclusa ➜ Refuge du Portillon</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refugio de la Renclusa au Refuge du Portillon</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>574d4668eb44937395115da70d5a30fd</ql:key>
|
<ql:key>574d4668eb44937395115da70d5a30fd</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -69644,8 +69578,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 17</name>
|
<name>Etape 17</name>
|
||||||
<desc><div>
|
<desc>Refuge du Portillon ➜ Refugio de Viados</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge du Portillon au Refugio de Viados</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>c93882c99ba4026293e355a549b84b71</ql:key>
|
<ql:key>c93882c99ba4026293e355a549b84b71</ql:key>
|
||||||
<ql:flags>7</ql:flags>
|
<ql:flags>7</ql:flags>
|
||||||
@@ -76190,8 +76123,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 18</name>
|
<name>Etape 18</name>
|
||||||
<desc><div>
|
<desc>Refugio de Viados ➜ Parzan</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refugio de Viados à Parzan</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>612d9c9dd01e11f75b5ae05547835536</ql:key>
|
<ql:key>612d9c9dd01e11f75b5ae05547835536</ql:key>
|
||||||
<ql:flags>7</ql:flags>
|
<ql:flags>7</ql:flags>
|
||||||
@@ -80071,8 +80003,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 18 - HRP original</name>
|
<name>Etape 18 - HRP original</name>
|
||||||
<desc><div>
|
<desc>Refugio de Viados ➜ Refuge de Barroude</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refugio de Viados au Refuge de Barroude</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>e5069e67b53d2880bdf1581a13b757c4</ql:key>
|
<ql:key>e5069e67b53d2880bdf1581a13b757c4</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -80549,8 +80480,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 19</name>
|
<name>Etape 19</name>
|
||||||
<desc><div>
|
<desc>Parzan ➜ l'Auberge le Maillet</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Parzan à l'Auberge le Maillet</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>651effda48dd9093dc73f3a8a24e2e40</ql:key>
|
<ql:key>651effda48dd9093dc73f3a8a24e2e40</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -84291,8 +84221,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 19 - HRP original</name>
|
<name>Etape 19 - HRP original</name>
|
||||||
<desc><div>
|
<desc>Refugio de Viados ➜ Refuge de Barroude</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refugio de Viados au Refuge de Barroude</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>99ae4a3b940c385481c4b42eb3b4a196</ql:key>
|
<ql:key>99ae4a3b940c385481c4b42eb3b4a196</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -85785,8 +85714,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 20</name>
|
<name>Etape 20</name>
|
||||||
<desc><div>
|
<desc>L'Auberge le Maillet ➜ Gavarnie</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">L'Auberge le Maillet à Gavarnie</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>b48c63d55bd654c8f93870a39571251a</ql:key>
|
<ql:key>b48c63d55bd654c8f93870a39571251a</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -89423,8 +89351,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 21</name>
|
<name>Etape 21</name>
|
||||||
<desc><div>
|
<desc>Gavarnie ➜ Refuge de Bayssellance</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Gavarnie au Refuge de Bayssellance</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>d158ee70a5de9e81361df2b4b64c8a4b</ql:key>
|
<ql:key>d158ee70a5de9e81361df2b4b64c8a4b</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -93612,8 +93539,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 21 - Variante Brèche de Roland</name>
|
<name>Etape 21 - Variante Brèche de Roland</name>
|
||||||
<desc><div>
|
<desc>Ascension Brèche de Roland</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ascension Brèche de Roland</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>5fc73a8c5b88608af36f38e0cf4a150f</ql:key>
|
<ql:key>5fc73a8c5b88608af36f38e0cf4a150f</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -99973,8 +99899,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 22</name>
|
<name>Etape 22</name>
|
||||||
<desc><div>
|
<desc>Refuge de Bayssellance ➜ Refuge Des Oulettes de Gaube + ascension Vignemale</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge de Bayssellance au Refuge Des Oulettes de Gaube et ascension Vignemale</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>7a5bc318d11418c4d75a50ecbf87c0ad</ql:key>
|
<ql:key>7a5bc318d11418c4d75a50ecbf87c0ad</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -102699,8 +102624,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 23</name>
|
<name>Etape 23</name>
|
||||||
<desc><div>
|
<desc>Refuge Des Oulettes de Gaube ➜ Refuge D'Arrémoulit</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge Des Oulettes de Gaube au Refuge D'Arrémoulit</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>1bde0c938a8befba899343806c9f4f7b</ql:key>
|
<ql:key>1bde0c938a8befba899343806c9f4f7b</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -111942,8 +111866,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 24</name>
|
<name>Etape 24</name>
|
||||||
<desc><div>
|
<desc>Refuge D'Arrémoulit ➜ Albergue Aysa</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refuge D'Arrémoulit à Albergue Aysa</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>e4b81d076b1c29b16dcd05b838f8c230</ql:key>
|
<ql:key>e4b81d076b1c29b16dcd05b838f8c230</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -113820,8 +113743,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 25</name>
|
<name>Etape 25</name>
|
||||||
<desc><div>
|
<desc>Albergue Aysa ➜ Camping du Lauzart</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Albergue Aysa au Camping du Lauzart</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>a5a91f642dafe534bcfb2a937396a17e</ql:key>
|
<ql:key>a5a91f642dafe534bcfb2a937396a17e</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -123049,8 +122971,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 25 - HRP original</name>
|
<name>Etape 25 - HRP original</name>
|
||||||
<desc><div>
|
<desc>Refugio Pepe Garces ➜ l'Ibón de Acherito</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Refugio Pepe Garces à l'Ibón de Acherito</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>b7eefd1505014117f27f73afad1872f4</ql:key>
|
<ql:key>b7eefd1505014117f27f73afad1872f4</ql:key>
|
||||||
<ql:flags>0</ql:flags>
|
<ql:flags>0</ql:flags>
|
||||||
@@ -124743,8 +124664,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 26</name>
|
<name>Etape 26</name>
|
||||||
<desc><div>
|
<desc>Camping du Lauzart ➜ Camping Asolaze</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Camping du Lauzart au Camping Asolaze</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>7a282ffe2293e1e5350313f348a34482</ql:key>
|
<ql:key>7a282ffe2293e1e5350313f348a34482</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -130075,8 +129995,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 27</name>
|
<name>Etape 27</name>
|
||||||
<desc><div>
|
<desc>Camping Asolaze ➜ Camping d’Iraty</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Camping Asolaze au Camping d’Iraty</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>7de5c4e4124cf7eeca85e540b1e5c309</ql:key>
|
<ql:key>7de5c4e4124cf7eeca85e540b1e5c309</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -140181,8 +140100,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 28</name>
|
<name>Etape 28</name>
|
||||||
<desc><div>
|
<desc>Camping d’Iraty ➜ Hostel Roncesvalles</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Camping d’Iraty à Hostel Roncesvalles</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>41bf55af75d1a405efcfcfa57fcea50b</ql:key>
|
<ql:key>41bf55af75d1a405efcfcfa57fcea50b</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -148278,8 +148196,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 29</name>
|
<name>Etape 29</name>
|
||||||
<desc><div>
|
<desc>Hostel Roncesvalles ➜ Elizondo</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hostel Roncesvalles à Elizondo</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>ca6af086e6d59f01497dbb2b1bf3985f</ql:key>
|
<ql:key>ca6af086e6d59f01497dbb2b1bf3985f</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -156767,8 +156684,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 30</name>
|
<name>Etape 30</name>
|
||||||
<desc><div>
|
<desc>Elizondo ➜ Camping La Petite Rhune</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Elizondo au Camping La Petite Rhune</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>695b939a623e6abc65ed717583435370</ql:key>
|
<ql:key>695b939a623e6abc65ed717583435370</ql:key>
|
||||||
<ql:flags>3</ql:flags>
|
<ql:flags>3</ql:flags>
|
||||||
@@ -165464,8 +165380,7 @@ This is waypoint no: 425</desc>
|
|||||||
</trk>
|
</trk>
|
||||||
<trk>
|
<trk>
|
||||||
<name>Etape 31</name>
|
<name>Etape 31</name>
|
||||||
<desc><div>
|
<desc>Camping La Petite Rhune ➜ Hendaye</desc>
|
||||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Camping La Petite Rhune à Hendaye</p></div></desc>
|
|
||||||
<extensions>
|
<extensions>
|
||||||
<ql:key>c3bc833d7a5fcb33e8698738d1e516f4</ql:key>
|
<ql:key>c3bc833d7a5fcb33e8698738d1e516f4</ql:key>
|
||||||
<ql:flags>6</ql:flags>
|
<ql:flags>6</ql:flags>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
"send": "Send"
|
"send": "Send"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"config": "Config",
|
"config": "Settings",
|
||||||
"create_success": "Created",
|
"create_success": "Created",
|
||||||
"delete_success": "Deleted",
|
"delete_success": "Deleted",
|
||||||
"save_success": "Saved",
|
"save_success": "Saved",
|
||||||
@@ -15,19 +15,19 @@
|
|||||||
"upload": "Upload"
|
"upload": "Upload"
|
||||||
},
|
},
|
||||||
"credits": {
|
"credits": {
|
||||||
"git": "Git Repository",
|
"git": "Git repository",
|
||||||
"license": "under GPLv3 license",
|
"license": "licensed under GPLv3",
|
||||||
"project": "Spotty Project"
|
"project": "$0 Project"
|
||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"confirmation": {
|
"confirmation": {
|
||||||
"body_1": "Thank you for checking in on my wanderings :). I'll make sure to keep you posted on my progress along the trail.",
|
"body_1": "Thank you for following my wanderings :). I'll make sure to keep you posted on my progress along the trail.",
|
||||||
"body_2": "I usually check-in once a day, plus sometimes on special events, like successful peak ascents. I am using a GPS-based device (PLB) which does not require phone reception to work. Thus the messages should be pretty frequent, but, being awestruck by the beauty of nature, I could also just forget to send a signal once in a while. So do not worry if you don't receive anything for a couple of days.",
|
"body_2": "I usually check in once a day, and sometimes at special moments too, like successful peak ascents. I'm using a GPS-based device (PLB), which doesn't require phone reception to work. So messages should be fairly frequent, but — being awestruck by the beauty of nature — I might also just forget to send a signal once in a while. So don't worry if you don't receive anything for a couple of days.",
|
||||||
"body_3": "If I've posted some pictures recently, you should also get them in the same email.",
|
"body_3": "If I've posted any pictures recently, you'll also find them in the same email.",
|
||||||
"conclusion": "See you down the road!",
|
"conclusion": "See you down the road!",
|
||||||
"preheader": "Thanks for keeping in touch!",
|
"preheader": "Thanks for keeping in touch!",
|
||||||
"signature": "--François",
|
"signature": "--François",
|
||||||
"subject": "Successful Registration",
|
"subject": "Registration confirmed",
|
||||||
"thanks_subject": "You're all set!"
|
"thanks_subject": "You're all set!"
|
||||||
},
|
},
|
||||||
"unsubscribe": "PS: Changed your mind?",
|
"unsubscribe": "PS: Changed your mind?",
|
||||||
@@ -40,19 +40,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"commit_db": "Issue committing to DB",
|
"commit_db": "Error committing to database",
|
||||||
"impossible_value": "Value \"$0\" is not possible for field \"$1\"",
|
"impossible_value": "Value \"$0\" is not valid for field \"$1\"",
|
||||||
"no_auth": "No authorization",
|
"no_auth": "Not authorized",
|
||||||
"unknown_field": "Field \"$0\" is unknown"
|
"unknown_field": "Unknown field \"$0\""
|
||||||
},
|
},
|
||||||
"feed": {
|
"feed": {
|
||||||
"counter": "#$0",
|
"counter": "#$0",
|
||||||
"id": "Feed ID",
|
"id": "Feed ID",
|
||||||
"last_update": "Last Spot Check",
|
"last_update": "Last Spot update",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"new": "New feed",
|
"new": "New feed",
|
||||||
"plural": "Feeds",
|
"plural": "Feeds",
|
||||||
"ref_id": "Ref. Feed ID",
|
"ref_id": "Feed reference ID",
|
||||||
"status": "Status"
|
"status": "Status"
|
||||||
},
|
},
|
||||||
"map": {
|
"map": {
|
||||||
@@ -63,40 +63,39 @@
|
|||||||
"otm": "Open Topo Map",
|
"otm": "Open Topo Map",
|
||||||
"outdoors": "Mapbox Outdoors",
|
"outdoors": "Mapbox Outdoors",
|
||||||
"satellite": "Satellite",
|
"satellite": "Satellite",
|
||||||
"see_on_google": "See on Google Maps",
|
"see_on_google": "View position on Google Maps",
|
||||||
"title": "Base Maps",
|
"title": "Base maps",
|
||||||
"usgs": "USGS"
|
"usgs": "USGS"
|
||||||
},
|
},
|
||||||
"media": {
|
"media": {
|
||||||
"add_on": "added on $0",
|
"click_watch": "Click to watch the video",
|
||||||
"click_watch": "Click to watch video",
|
|
||||||
"click_zoom": "Click to zoom",
|
"click_zoom": "Click to zoom",
|
||||||
"comment_update": "Comment of media \"$0\" updated",
|
"comment_update": "Comment for media \"$0\" updated",
|
||||||
"count": "Media $0 / $1",
|
|
||||||
"image": "Picture",
|
"image": "Picture",
|
||||||
"image_taken": "taken on $0",
|
"image_taken_on": "Taken on $0",
|
||||||
"images": "Pictures",
|
"images": "Pictures",
|
||||||
"nearby": "Nearby pictures",
|
"nearby": "Nearby pictures",
|
||||||
"no_id": "Missing Media ID in request",
|
"no_id": "Missing media ID in request",
|
||||||
|
"posted_on": "Posted on $0",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
"video_taken": "shot on $0"
|
"video_taken_on": "Shot on $0"
|
||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"locale": "en_NZ",
|
"locale": "en_NZ",
|
||||||
"page_og_desc": "Keep contact with François when he is off hiking"
|
"page_og_desc": "Stay in touch while I'm away hiking."
|
||||||
},
|
},
|
||||||
"newsletter": {
|
"newsletter": {
|
||||||
"email_exists": "This email is already subscribed. You can unsubscribe by clicking on the button above.",
|
"email_exists": "This email address is already subscribed. You can unsubscribe by clicking the button above.",
|
||||||
"email_placeholder": "my@email.com",
|
"email_placeholder": "my@email.com",
|
||||||
"invalid_email": "It doesn't look like an email",
|
"invalid_email": "This doesn't look like a valid email address",
|
||||||
"subscribe": "Subscribe",
|
"subscribe": "Subscribe",
|
||||||
"subscribed": "Thanks! You'll receive a confirmation email shortly",
|
"subscribed": "Thanks! You'll receive a confirmation email shortly.",
|
||||||
"subscribed_desc": "You're all set. We'll send you updates as soon as we get them.",
|
"subscribed_desc": "You're all set. I'll send you updates!",
|
||||||
"title": "Keep in touch!",
|
"title": "Keep in touch!",
|
||||||
"unknown_email": "Unknown email address",
|
"unknown_email": "Unknown email address",
|
||||||
"unsubscribe": "Unsubscribe",
|
"unsubscribe": "Unsubscribe",
|
||||||
"unsubscribed": "Done. No more junk mail from us",
|
"unsubscribed": "Done. No more junk mail from me.",
|
||||||
"unsubscribed_desc": "Write down your email address and we'll send you François' position as soon as we get it :)"
|
"unsubscribed_desc": "Enter your email address to receive my position as soon as I find it :)"
|
||||||
},
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"copy_to_clipboard": "Copy direct link to clipboard",
|
"copy_to_clipboard": "Copy direct link to clipboard",
|
||||||
@@ -106,17 +105,19 @@
|
|||||||
"new_message": "New message"
|
"new_message": "New message"
|
||||||
},
|
},
|
||||||
"project": {
|
"project": {
|
||||||
|
"wip": "In progress",
|
||||||
"code_name": "Code name",
|
"code_name": "Code name",
|
||||||
"end": "End",
|
"end": "End",
|
||||||
"hikes": "Hikes",
|
"hikes": "Hikes",
|
||||||
"id": "Project ID",
|
"id": "Project ID",
|
||||||
"mode": "Mode",
|
"mode": "Mode",
|
||||||
"modes": {
|
"modes": {
|
||||||
"blog": "Active Project",
|
"blog": "Active project",
|
||||||
"histo": "Archived Project",
|
"histo": "Archived project",
|
||||||
"previz": "Project in preparation"
|
"previz": "Project in preparation"
|
||||||
},
|
},
|
||||||
"new": "New Project",
|
"new": "New project",
|
||||||
|
"overview": "Overview",
|
||||||
"plural": "Projects",
|
"plural": "Projects",
|
||||||
"single": "Project",
|
"single": "Project",
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
@@ -125,7 +126,7 @@
|
|||||||
"spot": {
|
"spot": {
|
||||||
"id": "Spot ID",
|
"id": "Spot ID",
|
||||||
"model": "Model",
|
"model": "Model",
|
||||||
"name": "Spot Name",
|
"name": "Spot name",
|
||||||
"plural": "Spots",
|
"plural": "Spots",
|
||||||
"ref_id": "Ref. Spot ID"
|
"ref_id": "Ref. Spot ID"
|
||||||
},
|
},
|
||||||
@@ -135,19 +136,21 @@
|
|||||||
"elevation": "Elevation",
|
"elevation": "Elevation",
|
||||||
"elevation_gain": "Elevation gain",
|
"elevation_gain": "Elevation gain",
|
||||||
"elevation_loss": "Elevation loss",
|
"elevation_loss": "Elevation loss",
|
||||||
|
"from": "Start",
|
||||||
"legend": "Legend",
|
"legend": "Legend",
|
||||||
"segment_length": "Segment length",
|
"segment_length": "Segment length",
|
||||||
"type": "Track Type"
|
"to": "Finish",
|
||||||
|
"type": "Track type"
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"city": "$0 Time",
|
"city": "$0 time",
|
||||||
"date_time": "$0 at $1",
|
"date_time": "$0 at $1",
|
||||||
"local": "$0 Local Time",
|
"local": "$0 local time",
|
||||||
"user": "$0 Your Time",
|
"user": "$0 your time",
|
||||||
"zone": "Time Zone"
|
"zone": "Time zone"
|
||||||
},
|
},
|
||||||
"track": {
|
"track": {
|
||||||
"download": "Download GPX Track",
|
"download": "Download GPX track",
|
||||||
"hitchhiking": "Hitchhiking",
|
"hitchhiking": "Hitchhiking",
|
||||||
"main": "Main track",
|
"main": "Main track",
|
||||||
"off-track": "Off-track"
|
"off-track": "Off-track"
|
||||||
@@ -159,46 +162,47 @@
|
|||||||
"hour": "h"
|
"hour": "h"
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
|
"error": "Upload failed",
|
||||||
"media": {
|
"media": {
|
||||||
"exists": "Picture $0 already exists",
|
"exists": "Picture $0 already exists",
|
||||||
"title": "Picture & Video Uploads"
|
"title": "Picture and video uploads"
|
||||||
},
|
},
|
||||||
"mode_archived": "Project \"$0\" is archived. No upload allowed",
|
"mode_archived": "Project \"$0\" is archived. Uploads are not allowed.",
|
||||||
"position": {
|
"position": {
|
||||||
"new": "New Position",
|
"new": "New position",
|
||||||
"title": "Additional Position"
|
"title": "Add position"
|
||||||
},
|
},
|
||||||
"success": "$0 uploaded successfully"
|
"success": "$0 uploaded successfully"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"active": "Active Users",
|
"active": "Active users",
|
||||||
"clearance": "Clearance",
|
"clearance": "Clearance",
|
||||||
"id": "User ID",
|
"id": "User ID",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"name": "User Name"
|
"name": "User name"
|
||||||
},
|
},
|
||||||
"weather": {
|
"weather": {
|
||||||
"clear-day": "Cloud cover is less than 20% during daytime",
|
"clear-day": "Cloud cover is less than 20% during the day",
|
||||||
"clear-night": "Cloud cover is less than 20% during nighttime",
|
"clear-night": "Cloud cover is less than 20% during the night",
|
||||||
"cloudy": "Cloud cover is greater than 90%",
|
"cloudy": "Cloud cover is greater than 90%",
|
||||||
"fog": "Visibility is low (less than one kilometer or mile)",
|
"fog": "Visibility is low (less than 1km)",
|
||||||
"hail": "Hail showers",
|
"hail": "Hail showers",
|
||||||
"partly-cloudy-day": "Cloud cover is greater than 20% during daytime",
|
"partly-cloudy-day": "Cloud cover is greater than 20% during the day",
|
||||||
"partly-cloudy-night": "Cloud cover is greater than 20% during nighttime",
|
"partly-cloudy-night": "Cloud cover is greater than 20% during the night",
|
||||||
"rain": "Amount of rainfall is greater than zero",
|
"rain": "Rainfall is greater than zero",
|
||||||
"rain-snow": "Snow and rain showers",
|
"rain-snow": "Snow and rain showers",
|
||||||
"rain-snow-showers-day": "Possible rain/snow throughout the day",
|
"rain-snow-showers-day": "Possible rain/snow throughout the day",
|
||||||
"rain-snow-showers-night": "Possible rain/snow throughout the night",
|
"rain-snow-showers-night": "Possible rain/snow throughout the night",
|
||||||
"showers-day": "Rain showers during the day",
|
"showers-day": "Rain showers during the day",
|
||||||
"showers-night": "Rain showers during the night",
|
"showers-night": "Rain showers during the night",
|
||||||
"sleet": "Sleet",
|
"sleet": "Sleet",
|
||||||
"snow": "Amount of snow is greater than zero",
|
"snow": "Snowfall is greater than zero",
|
||||||
"snow-showers-day": "Periods of snow during the day",
|
"snow-showers-day": "Periods of snow during the day",
|
||||||
"snow-showers-night": "Periods of snow during the night",
|
"snow-showers-night": "Periods of snow during the night",
|
||||||
"thunder": "Thunderstorms",
|
"thunder": "Thunderstorms",
|
||||||
"thunder-rain": "Thunderstorms throughout the day or night",
|
"thunder-rain": "Thunderstorms throughout the day or night",
|
||||||
"thunder-showers-day": "Possible thunderstorms throughout the day",
|
"thunder-showers-day": "Possible thunderstorms throughout the day",
|
||||||
"thunder-showers-night": "Possible thunderstorms throughout the night",
|
"thunder-showers-night": "Possible thunderstorms throughout the night",
|
||||||
"wind": "Wind speed is high (greater than 30 kph or mph)"
|
"wind": "Wind speed is high (greater than 30 km/h)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,13 +17,13 @@
|
|||||||
"credits": {
|
"credits": {
|
||||||
"git": "Repositorio de Git",
|
"git": "Repositorio de Git",
|
||||||
"license": "bajo licencia GPLv3",
|
"license": "bajo licencia GPLv3",
|
||||||
"project": "Proyecto Spotty"
|
"project": "Proyecto $0"
|
||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"confirmation": {
|
"confirmation": {
|
||||||
"body_1": "Os agradezco mucho que sigais mi proyecto, y os intereseis de la evolucion. Os prometo que os mantendré informados sobre mi progreso.",
|
"body_1": "Te agradezco mucho que sigas mi proyecto y que te intereses por su evolución. Te prometo que te mantendré informado sobre mi progreso.",
|
||||||
"body_2": "Normalmente envío un mensaje una vez al día. Cuando voy a lugares guays, envío uno extra (cimas, ese tipo de cosas). Estoy usando una dispositivo GPS para enviar la señal, por lo que no necesito una red telefónica para que funcione. Sin embargo, puede haber ocasiones en las que presione el botón. Por lo tanto, no se preocupe si no recibe mensajes durante uno o dos días.",
|
"body_2": "Normalmente envío un mensaje una vez al día. Cuando voy a lugares especiales, envío uno extra (cimas, ese tipo de cosas). Uso un dispositivo GPS para enviar la señal, así que no necesito cobertura telefónica para que funcione. Sin embargo, puede haber ocasiones en las que no presione el botón. Por lo tanto, no te preocupes si no recibes mensajes durante uno o dos días.",
|
||||||
"body_3": "Cuando añada fotos en la página, también deberás encontrarlas en este correo electrónico.",
|
"body_3": "Cuando añada fotos a la página, también las encontrarás en este correo electrónico.",
|
||||||
"conclusion": "¡Nos vemos en el camino!",
|
"conclusion": "¡Nos vemos en el camino!",
|
||||||
"preheader": "¡Gracias por mantenerte en contacto!",
|
"preheader": "¡Gracias por mantenerte en contacto!",
|
||||||
"signature": "--François",
|
"signature": "--François",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"thanks_subject": "¡Hecho!"
|
"thanks_subject": "¡Hecho!"
|
||||||
},
|
},
|
||||||
"unsubscribe": "PD: ¿Demasiados correos electrónicos?",
|
"unsubscribe": "PD: ¿Demasiados correos electrónicos?",
|
||||||
"unsubscribe_button": "Desinscribirse",
|
"unsubscribe_button": "Darse de baja",
|
||||||
"update": {
|
"update": {
|
||||||
"latest_news": "Últimas noticias:",
|
"latest_news": "Últimas noticias:",
|
||||||
"preheader": "¡Nueva posición!",
|
"preheader": "¡Nueva posición!",
|
||||||
@@ -41,12 +41,12 @@
|
|||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"commit_db": "Error SQL",
|
"commit_db": "Error SQL",
|
||||||
"impossible_value": "Valor \"$0\" no es posible para campo \"$1\"",
|
"impossible_value": "El valor \"$0\" no es posible para el campo \"$1\"",
|
||||||
"no_auth": "No autorización",
|
"no_auth": "Sin autorización",
|
||||||
"unknown_field": "Campo \"$0\" desconocido"
|
"unknown_field": "Campo \"$0\" desconocido"
|
||||||
},
|
},
|
||||||
"feed": {
|
"feed": {
|
||||||
"counter": "No. $0",
|
"counter": "N.º $0",
|
||||||
"id": "ID Feed",
|
"id": "ID Feed",
|
||||||
"last_update": "Última actualización de Spot",
|
"last_update": "Última actualización de Spot",
|
||||||
"name": "Descripción",
|
"name": "Descripción",
|
||||||
@@ -68,35 +68,34 @@
|
|||||||
"usgs": "USGS"
|
"usgs": "USGS"
|
||||||
},
|
},
|
||||||
"media": {
|
"media": {
|
||||||
"add_on": "Agregado el $0",
|
"click_watch": "Haz clic para ver el vídeo",
|
||||||
"click_watch": "Haz clic para ver el video",
|
|
||||||
"click_zoom": "Haz clic para ampliar",
|
"click_zoom": "Haz clic para ampliar",
|
||||||
"comment_update": "Comentario \"$0\" actualizado",
|
"comment_update": "Comentario \"$0\" actualizado",
|
||||||
"count": "Media $0 de $1",
|
|
||||||
"image": "Foto",
|
"image": "Foto",
|
||||||
"image_taken": "Foto tomada el $0",
|
"image_taken_on": "Foto tomada el $0",
|
||||||
"images": "Fotos",
|
"images": "Fotos",
|
||||||
"nearby": "Fotos cercanas",
|
"nearby": "Fotos cercanas",
|
||||||
"no_id": "Falta el ID del sujeto",
|
"no_id": "Falta el ID del archivo multimedia",
|
||||||
"video": "Video",
|
"posted_on": "Añadido el $0",
|
||||||
"video_taken": "Video filmado el $0"
|
"video": "Vídeo",
|
||||||
|
"video_taken_on": "Vídeo grabado el $0"
|
||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"locale": "es_ES",
|
"locale": "es_ES",
|
||||||
"page_og_desc": "Mantente en contacto con François durante sus aventuras a la montaña"
|
"page_og_desc": "Mantente en contacto conmigo durante mis aventuras en la montaña."
|
||||||
},
|
},
|
||||||
"newsletter": {
|
"newsletter": {
|
||||||
"email_exists": "Esta dirección de correo electrónico ya está registrada. Puedes darte de baja haciendo clic en el botón de arriba.",
|
"email_exists": "Esta dirección de correo electrónico ya está registrada. Puedes darte de baja haciendo clic en el botón de arriba.",
|
||||||
"email_placeholder": "nombre@email.com",
|
"email_placeholder": "nombre@email.com",
|
||||||
"invalid_email": "Esto no parece una dirección de correo electrónico",
|
"invalid_email": "Esto no parece una dirección de correo electrónico",
|
||||||
"subscribe": "Suscribir",
|
"subscribe": "Suscribirse",
|
||||||
"subscribed": "¡Gracias! Recibirás un correo electrónico de confirmación",
|
"subscribed": "¡Gracias! Recibirás un correo electrónico de confirmación.",
|
||||||
"subscribed_desc": "Todo esta listo. Te enviaremos noticias frescas en cuanto las recibamos. Prometido...",
|
"subscribed_desc": "Todo está listo. Te enviaré noticias frescas en cuanto las reciba. Prometido...",
|
||||||
"title": "Mantenerse en contacto",
|
"title": "Mantente en contacto",
|
||||||
"unknown_email": "Dirección de email desconocida",
|
"unknown_email": "Dirección de correo electrónico desconocida",
|
||||||
"unsubscribe": "Desinscribirse",
|
"unsubscribe": "Darse de baja",
|
||||||
"unsubscribed": "Está hecho. ¡No más spam!",
|
"unsubscribed": "Listo. ¡No más spam!",
|
||||||
"unsubscribed_desc": "Anade tu dirección de correo electrónico y te enviaremos la posicion actualizada de François tan pronto como la recibamos :)"
|
"unsubscribed_desc": "Añade tu dirección de correo electrónico y te enviaré mi posición actualizada tan pronto como la reciba :)"
|
||||||
},
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"copy_to_clipboard": "Copiar el enlace",
|
"copy_to_clipboard": "Copiar el enlace",
|
||||||
@@ -106,10 +105,11 @@
|
|||||||
"new_message": "Mensaje nuevo"
|
"new_message": "Mensaje nuevo"
|
||||||
},
|
},
|
||||||
"project": {
|
"project": {
|
||||||
|
"wip": "En curso",
|
||||||
"code_name": "Nombre clave",
|
"code_name": "Nombre clave",
|
||||||
"end": "Fin",
|
"end": "Fin",
|
||||||
"hikes": "Senderos",
|
"hikes": "Senderos",
|
||||||
"id": "Proyecto ID",
|
"id": "ID del proyecto",
|
||||||
"mode": "Modo",
|
"mode": "Modo",
|
||||||
"modes": {
|
"modes": {
|
||||||
"blog": "Proyecto activo",
|
"blog": "Proyecto activo",
|
||||||
@@ -117,17 +117,18 @@
|
|||||||
"previz": "Proyecto en preparación"
|
"previz": "Proyecto en preparación"
|
||||||
},
|
},
|
||||||
"new": "Nuevo proyecto",
|
"new": "Nuevo proyecto",
|
||||||
|
"overview": "Vista general",
|
||||||
"plural": "Proyectos",
|
"plural": "Proyectos",
|
||||||
"single": "Proyecto",
|
"single": "Proyecto",
|
||||||
"start": "Inicio",
|
"start": "Inicio",
|
||||||
"update_messages": "Actualizar los mensajes del proyecto"
|
"update_messages": "Actualizar los mensajes del proyecto"
|
||||||
},
|
},
|
||||||
"spot": {
|
"spot": {
|
||||||
"id": "ID Spot",
|
"id": "ID de Spot",
|
||||||
"model": "Modelo",
|
"model": "Modelo",
|
||||||
"name": "Spot",
|
"name": "Spot",
|
||||||
"plural": "Spots",
|
"plural": "Spots",
|
||||||
"ref_id": "ID Spot ref."
|
"ref_id": "ID de referencia de Spot"
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"duration": "Duración",
|
"duration": "Duración",
|
||||||
@@ -135,40 +136,43 @@
|
|||||||
"elevation": "Elevación",
|
"elevation": "Elevación",
|
||||||
"elevation_gain": "Ascenso acumulado",
|
"elevation_gain": "Ascenso acumulado",
|
||||||
"elevation_loss": "Descenso acumulado",
|
"elevation_loss": "Descenso acumulado",
|
||||||
|
"from": "Inicio",
|
||||||
"legend": "Leyenda",
|
"legend": "Leyenda",
|
||||||
"segment_length": "Tamaño del segmento",
|
"segment_length": "Tamaño del segmento",
|
||||||
|
"to": "Fin",
|
||||||
"type": "Tipo de sendero"
|
"type": "Tipo de sendero"
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"city": "Hora de $0",
|
"city": "Hora en $0",
|
||||||
"date_time": "$0 a la $1",
|
"date_time": "$0 a las $1",
|
||||||
"local": "$0 hora local",
|
"local": "$0 hora local",
|
||||||
"user": "$0 en tu zona horaria",
|
"user": "$0 en tu zona horaria",
|
||||||
"zone": "Huso horario"
|
"zone": "Huso horario"
|
||||||
},
|
},
|
||||||
"track": {
|
"track": {
|
||||||
"download": "Descarga la ruta GPX",
|
"download": "Descargar la ruta GPX",
|
||||||
"hitchhiking": "Autostop",
|
"hitchhiking": "Autostop",
|
||||||
"main": "Camino principal",
|
"main": "Camino principal",
|
||||||
"off-track": "Variante"
|
"off-track": "Variante"
|
||||||
},
|
},
|
||||||
"unit": {
|
"unit": {
|
||||||
"day": "Día",
|
"day": "día",
|
||||||
"day_short": "D",
|
"day_short": "D",
|
||||||
"days": "Días",
|
"days": "días",
|
||||||
"hour": "h"
|
"hour": "h"
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
|
"error": "Error al subir el archivo",
|
||||||
"media": {
|
"media": {
|
||||||
"exists": "La imagen $0 ya existe",
|
"exists": "La imagen $0 ya existe",
|
||||||
"title": "Cargar fotos y videos"
|
"title": "Cargar fotos y vídeos"
|
||||||
},
|
},
|
||||||
"mode_archived": "El proyecto \"$0\" esta archivado. No se puede cargar",
|
"mode_archived": "El proyecto \"$0\" está archivado. No se puede cargar.",
|
||||||
"position": {
|
"position": {
|
||||||
"new": "Nueva posición",
|
"new": "Nueva posición",
|
||||||
"title": "Subir posición"
|
"title": "Subir posición"
|
||||||
},
|
},
|
||||||
"success": "$0 ha sido subido"
|
"success": "$0 se ha subido correctamente."
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"active": "Usuarios activos",
|
"active": "Usuarios activos",
|
||||||
@@ -181,7 +185,7 @@
|
|||||||
"clear-day": "La nubosidad es inferior al 20 % durante el día",
|
"clear-day": "La nubosidad es inferior al 20 % durante el día",
|
||||||
"clear-night": "La nubosidad es inferior al 20 % durante la noche",
|
"clear-night": "La nubosidad es inferior al 20 % durante la noche",
|
||||||
"cloudy": "La nubosidad es superior al 90 %",
|
"cloudy": "La nubosidad es superior al 90 %",
|
||||||
"fog": "La visibilidad es baja (menos de un kilómetro o una milla)",
|
"fog": "La visibilidad es baja (menos de 1km)",
|
||||||
"hail": "Chubascos de granizo",
|
"hail": "Chubascos de granizo",
|
||||||
"partly-cloudy-day": "La nubosidad es superior al 20 % durante el día",
|
"partly-cloudy-day": "La nubosidad es superior al 20 % durante el día",
|
||||||
"partly-cloudy-night": "La nubosidad es superior al 20 % durante la noche",
|
"partly-cloudy-night": "La nubosidad es superior al 20 % durante la noche",
|
||||||
@@ -199,6 +203,6 @@
|
|||||||
"thunder-rain": "Tormentas durante el día o la noche",
|
"thunder-rain": "Tormentas durante el día o la noche",
|
||||||
"thunder-showers-day": "Posibles tormentas durante todo el día",
|
"thunder-showers-day": "Posibles tormentas durante todo el día",
|
||||||
"thunder-showers-night": "Posibles tormentas durante toda la noche",
|
"thunder-showers-night": "Posibles tormentas durante toda la noche",
|
||||||
"wind": "La velocidad del viento es alta (más de 30 km/h o mph)"
|
"wind": "La velocidad del viento es alta (más de 30 km/h)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,26 +11,26 @@
|
|||||||
"delete_success": "Supprimé",
|
"delete_success": "Supprimé",
|
||||||
"save_success": "Sauvegardé",
|
"save_success": "Sauvegardé",
|
||||||
"title": "Administration",
|
"title": "Administration",
|
||||||
"toolbox": "Boite à outils",
|
"toolbox": "Boîte à outils",
|
||||||
"upload": "Uploader"
|
"upload": "Téléverser"
|
||||||
},
|
},
|
||||||
"credits": {
|
"credits": {
|
||||||
"git": "Dépôt Git",
|
"git": "Dépôt Git",
|
||||||
"license": "sous licence GPLv3",
|
"license": "sous licence GPLv3",
|
||||||
"project": "Projet Spotty"
|
"project": "Projet $0"
|
||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"confirmation": {
|
"confirmation": {
|
||||||
"body_1": "C'est gentil de venir voir où j'en suis. Promis, je vous tiendrais au courant de mon avancée.",
|
"body_1": "C'est gentil de venir voir où j'en suis. Promis, je te tiendrai au courant de mon avancée.",
|
||||||
"body_2": "En général, j'envoie un message une fois par jour. Lorsque je passe à des endroits sympas, j'en envoie un supplémentaire (ascension de sommets, ce genre de choses). J'utilise une balise GPS pour envoyer le signal, je n'ai donc pas besoin de réseau téléphonique pour que cela fonctionne. Cependant, il peut m'arriver d'appuyer sur le bouton. Donc pas de raison de s'inquiéter si vous ne recevez pas de messages pendant une journée ou deux.",
|
"body_2": "En général, j'envoie un message une fois par jour. Lorsque je passe par des endroits sympas, j'en envoie un supplémentaire (sommets, ce genre de choses). J'utilise une balise GPS pour envoyer le signal, je n'ai donc pas besoin de réseau téléphonique pour que cela fonctionne. Cependant, il peut m'arriver d'oublier d'appuyer sur le bouton. Donc pas de raison de t'inquiéter si tu ne reçois pas de messages pendant une journée ou deux.",
|
||||||
"body_3": "Si j'ai ajouté des photos sur le site récemment, vous devriez aussi les retrouver dans cet email.",
|
"body_3": "Si j'ai ajouté des photos sur le site récemment, tu devrais aussi les retrouver dans cet e-mail.",
|
||||||
"conclusion": "A bientôt sur les chemins !",
|
"conclusion": "À bientôt sur les chemins !",
|
||||||
"preheader": "Merci de rester en contact !",
|
"preheader": "Merci de rester en contact !",
|
||||||
"signature": "--François",
|
"signature": "--François",
|
||||||
"subject": "Confirmation",
|
"subject": "Confirmation",
|
||||||
"thanks_subject": "C'est tout bon !"
|
"thanks_subject": "C'est tout bon !"
|
||||||
},
|
},
|
||||||
"unsubscribe": "PS: Trop d'emails ?",
|
"unsubscribe": "PS : Trop d'e-mails ?",
|
||||||
"unsubscribe_button": "Se désinscrire",
|
"unsubscribe_button": "Se désinscrire",
|
||||||
"update": {
|
"update": {
|
||||||
"latest_news": "Dernières nouvelles :",
|
"latest_news": "Dernières nouvelles :",
|
||||||
@@ -40,14 +40,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"commit_db": "Error lors de la requête SQL",
|
"commit_db": "Erreur lors de la requête SQL",
|
||||||
"impossible_value": "La valeur \"$0\" n'est pas possible pour le champ \"$1\"",
|
"impossible_value": "La valeur \"$0\" n'est pas possible pour le champ \"$1\"",
|
||||||
"no_auth": "Pas d'authorisation",
|
"no_auth": "Pas d'autorisation",
|
||||||
"unknown_field": "Champ \"$0\" inconnu"
|
"unknown_field": "Champ \"$0\" inconnu"
|
||||||
},
|
},
|
||||||
"feed": {
|
"feed": {
|
||||||
"counter": "N°$0",
|
"counter": "N°$0",
|
||||||
"id": "ID Feed",
|
"id": "ID feed",
|
||||||
"last_update": "Dernière vérification Spot",
|
"last_update": "Dernière vérification Spot",
|
||||||
"name": "Description",
|
"name": "Description",
|
||||||
"new": "Nouveau feed",
|
"new": "Nouveau feed",
|
||||||
@@ -68,44 +68,44 @@
|
|||||||
"usgs": "USGS"
|
"usgs": "USGS"
|
||||||
},
|
},
|
||||||
"media": {
|
"media": {
|
||||||
"add_on": "ajoutée le $0",
|
"click_watch": "Cliquer pour voir la vidéo",
|
||||||
"click_watch": "Click pour voir la vidéo",
|
"click_zoom": "Cliquer pour zoomer",
|
||||||
"click_zoom": "Click pour zoomer",
|
"comment_update": "Commentaire du média \"$0\" mis à jour",
|
||||||
"comment_update": "Commentaire du media \"$0\" mis-à-jour",
|
|
||||||
"count": "Média $0 sur $1",
|
|
||||||
"image": "Photo",
|
"image": "Photo",
|
||||||
"image_taken": "prise le $0",
|
"image_taken_on": "Prise le $0",
|
||||||
"images": "Photos",
|
"images": "Photos",
|
||||||
"nearby": "Photos prises dans le coin",
|
"nearby": "Photos prises dans le coin",
|
||||||
"no_id": "ID du média manquant",
|
"no_id": "ID du média manquant",
|
||||||
|
"posted_on": "Ajouté le $0",
|
||||||
"video": "Vidéo",
|
"video": "Vidéo",
|
||||||
"video_taken": "filmée le $0"
|
"video_taken_on": "Filmé le $0"
|
||||||
},
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"locale": "fr_CH",
|
"locale": "fr_CH",
|
||||||
"page_og_desc": "Gardez le contact avec François lorsqu'il part sur les chemins"
|
"page_og_desc": "Garde le contact lorsque je suis sur les chemins."
|
||||||
},
|
},
|
||||||
"newsletter": {
|
"newsletter": {
|
||||||
"email_exists": "Cette adresse email est déjà enregistrée. Vous pouvez vous désinscrire en cliquant sur le bouton ci-dessus.",
|
"email_exists": "Cette adresse e-mail est déjà enregistrée. Tu peux te désinscrire en cliquant sur le bouton ci-dessus.",
|
||||||
"email_placeholder": "mon@email.com",
|
"email_placeholder": "mon@email.com",
|
||||||
"invalid_email": "Ceci ne ressemble pas à une adresse email",
|
"invalid_email": "Ceci ne ressemble pas à une adresse e-mail",
|
||||||
"subscribe": "S'abonner",
|
"subscribe": "S'abonner",
|
||||||
"subscribed": "Merci ! Tu vas recevoir un email de confirmation très bientôt",
|
"subscribed": "Merci ! Tu vas recevoir un e-mail de confirmation très bientôt.",
|
||||||
"subscribed_desc": "C'est tout bon. On t'envoie des nouvelles fraiches dès qu'on les reçoit. Parole de scout.",
|
"subscribed_desc": "C'est tout bon. Je t'enverrai des nouvelles fraîches. Parole de scout.",
|
||||||
"title": "Rester en contact",
|
"title": "Rester en contact",
|
||||||
"unknown_email": "Adresse email inconnue",
|
"unknown_email": "Adresse e-mail inconnue",
|
||||||
"unsubscribe": "Se désinscrire",
|
"unsubscribe": "Se désinscrire",
|
||||||
"unsubscribed": "C'est fait. Fini le spam!",
|
"unsubscribed": "C'est fait. Fini le spam !",
|
||||||
"unsubscribed_desc": "Ajoute ton adresse email et on t'enverra la nouvelle position de François dès qu'on la reçoit :)"
|
"unsubscribed_desc": "Ajoute ton adresse e-mail et je t'enverrai ma nouvelle position dès que je la trouve :)"
|
||||||
},
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"copy_to_clipboard": "Copie le lien dans le presse-papier",
|
"copy_to_clipboard": "Copier le lien dans le presse-papiers",
|
||||||
"link_copied": "Lien copié !",
|
"link_copied": "Lien copié !",
|
||||||
"message": "Message",
|
"message": "Message",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"new_message": "Nouveau message"
|
"new_message": "Nouveau message"
|
||||||
},
|
},
|
||||||
"project": {
|
"project": {
|
||||||
|
"wip": "En cours",
|
||||||
"code_name": "Nom de code",
|
"code_name": "Nom de code",
|
||||||
"end": "Arrivée",
|
"end": "Arrivée",
|
||||||
"hikes": "Randonnées",
|
"hikes": "Randonnées",
|
||||||
@@ -117,6 +117,7 @@
|
|||||||
"previz": "Projet en cours de préparation"
|
"previz": "Projet en cours de préparation"
|
||||||
},
|
},
|
||||||
"new": "Nouveau projet",
|
"new": "Nouveau projet",
|
||||||
|
"overview": "Vue d'ensemble",
|
||||||
"plural": "Projets",
|
"plural": "Projets",
|
||||||
"single": "Projet",
|
"single": "Projet",
|
||||||
"start": "Départ",
|
"start": "Départ",
|
||||||
@@ -135,21 +136,23 @@
|
|||||||
"elevation": "Dénivelé",
|
"elevation": "Dénivelé",
|
||||||
"elevation_gain": "Dénivelé positif",
|
"elevation_gain": "Dénivelé positif",
|
||||||
"elevation_loss": "Dénivelé négatif",
|
"elevation_loss": "Dénivelé négatif",
|
||||||
|
"from": "Départ",
|
||||||
"legend": "Légende",
|
"legend": "Légende",
|
||||||
"segment_length": "Taille du segment",
|
"segment_length": "Taille du segment",
|
||||||
|
"to": "Arrivée",
|
||||||
"type": "Type de rando"
|
"type": "Type de rando"
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"city": "heure de $0",
|
"city": "Heure à $0",
|
||||||
"date_time": "$0 à $1",
|
"date_time": "$0 à $1",
|
||||||
"local": "$0 heure locale",
|
"local": "$0 heure locale",
|
||||||
"user": "$0 dans votre fuseau horaire",
|
"user": "$0 dans ton fuseau horaire",
|
||||||
"zone": "Fuseau horaire"
|
"zone": "Fuseau horaire"
|
||||||
},
|
},
|
||||||
"track": {
|
"track": {
|
||||||
"download": "Télécharger la trace GPX",
|
"download": "Télécharger la trace GPX",
|
||||||
"hitchhiking": "Hors rando",
|
"hitchhiking": "Auto-stop",
|
||||||
"main": "Trajet principal",
|
"main": "Itinéraire",
|
||||||
"off-track": "Variante"
|
"off-track": "Variante"
|
||||||
},
|
},
|
||||||
"unit": {
|
"unit": {
|
||||||
@@ -159,21 +162,22 @@
|
|||||||
"hour": "h"
|
"hour": "h"
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
|
"error": "Erreur lors du téléversement",
|
||||||
"media": {
|
"media": {
|
||||||
"exists": "l'image $0 existe déjà",
|
"exists": "L'image $0 existe déjà",
|
||||||
"title": "Uploader photos & vidéos"
|
"title": "Téléverser des photos et vidéos"
|
||||||
},
|
},
|
||||||
"mode_archived": "Le projet \"$0\" a été archivé. Aucun upload possible",
|
"mode_archived": "Le projet \"$0\" a été archivé. Aucun téléversement possible.",
|
||||||
"position": {
|
"position": {
|
||||||
"new": "Nouvelle position",
|
"new": "Nouvelle position",
|
||||||
"title": "Position supplémentaire"
|
"title": "Position supplémentaire"
|
||||||
},
|
},
|
||||||
"success": "$0 a été uploadé"
|
"success": "$0 a été téléversé"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"active": "Utilisateurs actifs",
|
"active": "Utilisateurs actifs",
|
||||||
"clearance": "Niveau d'autorisation",
|
"clearance": "Niveau d'autorisation",
|
||||||
"id": "ID Utilisateur",
|
"id": "ID utilisateur",
|
||||||
"language": "Langue",
|
"language": "Langue",
|
||||||
"name": "Nom"
|
"name": "Nom"
|
||||||
},
|
},
|
||||||
@@ -181,7 +185,7 @@
|
|||||||
"clear-day": "La couverture nuageuse est inférieure à 20 % pendant la journée",
|
"clear-day": "La couverture nuageuse est inférieure à 20 % pendant la journée",
|
||||||
"clear-night": "La couverture nuageuse est inférieure à 20 % pendant la nuit",
|
"clear-night": "La couverture nuageuse est inférieure à 20 % pendant la nuit",
|
||||||
"cloudy": "La couverture nuageuse est supérieure à 90 %",
|
"cloudy": "La couverture nuageuse est supérieure à 90 %",
|
||||||
"fog": "La visibilité est faible (moins d’un kilomètre ou d’un mile)",
|
"fog": "La visibilité est faible (moins de 1km)",
|
||||||
"hail": "Averses de grêle",
|
"hail": "Averses de grêle",
|
||||||
"partly-cloudy-day": "La couverture nuageuse est supérieure à 20 % pendant la journée",
|
"partly-cloudy-day": "La couverture nuageuse est supérieure à 20 % pendant la journée",
|
||||||
"partly-cloudy-night": "La couverture nuageuse est supérieure à 20 % pendant la nuit",
|
"partly-cloudy-night": "La couverture nuageuse est supérieure à 20 % pendant la nuit",
|
||||||
@@ -199,6 +203,6 @@
|
|||||||
"thunder-rain": "Orages tout au long de la journée ou de la nuit",
|
"thunder-rain": "Orages tout au long de la journée ou de la nuit",
|
||||||
"thunder-showers-day": "Orages possibles tout au long de la journée",
|
"thunder-showers-day": "Orages possibles tout au long de la journée",
|
||||||
"thunder-showers-night": "Orages possibles tout au long de la nuit",
|
"thunder-showers-night": "Orages possibles tout au long de la nuit",
|
||||||
"wind": "La vitesse du vent est élevée (plus de 30 km/h ou mph)"
|
"wind": "La vitesse du vent est élevée (plus de 30 km/h)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<span style="color: transparent; display: none !important; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">[#]lang:email.confirmation.preheader[#]</span>
|
<span style="color: transparent; display: none !important; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">[#]lang:email.confirmation.preheader[#]</span>
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="width:100%;max-width:600px;">
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="width:100%;max-width:600px;">
|
||||||
<tr>
|
<tr>
|
||||||
<td width="20%"><img src="[#]local_server[#]images/icons/mstile-144x144.png" width="90%" border="0" alt="logo" /></td>
|
<td width="20%"><img src="[#]local_server[#]assets/images/icons/favicon-96x96.png" width="90%" border="0" alt="logo" /></td>
|
||||||
<td><h1>[#]lang:email.confirmation.thanks_subject[#]</h1></td>
|
<td><h1>[#]lang:email.confirmation.thanks_subject[#]</h1></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<span style="color: transparent; display: none !important; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">[#]lang:email.update.preheader[#]</span>
|
<span style="color: transparent; display: none !important; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">[#]lang:email.update.preheader[#]</span>
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="width:100%;max-width:600px;">
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="width:100%;max-width:600px;">
|
||||||
<tr>
|
<tr>
|
||||||
<td width="20%"><img src="[#]local_server[#]images/icons/mstile-144x144.png" width="90%" border="0" alt="logo" /></td>
|
<td width="20%"><img src="[#]local_server[#]assets/images/icons/favicon-96x96.png" width="90%" border="0" alt="logo" /></td>
|
||||||
<td><h1>[#]lang:email.update.title[#] [#]type[#] #[#]displayed_id[#]</h1></td>
|
<td><h1>[#]lang:email.update.title[#] [#]type[#] #[#]displayed_id[#]</h1></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
29
resources/masks/index.html
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="[#]language[#]">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="description" content="[#]lang:meta.page_og_desc[#]">
|
||||||
|
<meta property="og:title" content="[#]title[#]" />
|
||||||
|
<meta property="og:description" content="[#]lang:meta.page_og_desc[#]" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="[#]server[#]" />
|
||||||
|
<meta property="og:image" content="assets/images/icons/ogp.svg?v=20260528" />
|
||||||
|
<meta property="og:locale" content="[#]lang:meta.locale[#]" />
|
||||||
|
<link rel="icon" type="image/png" href="assets/images/icons/favicon-96x96.png?v=20260528" sizes="96x96" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="assets/images/icons/favicon.svg?v=20260528" />
|
||||||
|
<link rel="shortcut icon" href="assets/images/icons/favicon.ico?v=20260528" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="assets/images/icons/apple-touch-icon.png?v=20260528" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="[#]title[#]" />
|
||||||
|
<link rel="manifest" href="assets/images/icons/site.webmanifest?v=20260528" />
|
||||||
|
<meta name="theme-color" content="#081B19">
|
||||||
|
<script id="app-config" type="application/json">[#]app_config[#]</script>
|
||||||
|
<title>[#]title[#]</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container"></div>
|
||||||
|
<!-- [PART] entrypoint [START] -->
|
||||||
|
<script defer src="[#]filename[#]"></script>
|
||||||
|
<!-- [PART] entrypoint [END] -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { defineAsyncComponent } from 'vue';
|
||||||
import Project from '@components/project';
|
import Project from '@components/project';
|
||||||
import Admin from '@components/admin';
|
|
||||||
import Upload from '@components/upload';
|
|
||||||
|
|
||||||
const aoRoutes = {
|
const aoRoutes = {
|
||||||
'project': Project,
|
'project': Project, //Merge app.js and project.js calls to avoid extra http request on inital page
|
||||||
'admin': Admin,
|
'admin': defineAsyncComponent(() => import(/* webpackChunkName: "admin" */ '@components/admin')),
|
||||||
'upload': Upload
|
'upload': defineAsyncComponent(() => import(/* webpackChunkName: "upload" */ '@components/upload'))
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
hash: {page: '', items: []},
|
hash: {page: '', items: [], prev: {}},
|
||||||
consts: this.appConfig.consts,
|
consts: this.appConfig.consts,
|
||||||
mobile: false
|
mobile: false
|
||||||
};
|
};
|
||||||
@@ -21,7 +20,9 @@ export default {
|
|||||||
return {
|
return {
|
||||||
hash: this.hash,
|
hash: this.hash,
|
||||||
consts: this.consts,
|
consts: this.consts,
|
||||||
isMobile: () => this.isMobile()
|
isMobile: () => this.isMobile(),
|
||||||
|
getAnchor: this.getAnchor,
|
||||||
|
getPrevAnchor: () => this.getPrevAnchor(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -29,30 +30,35 @@ export default {
|
|||||||
return aoRoutes[this.hash.page];
|
return aoRoutes[this.hash.page];
|
||||||
},
|
},
|
||||||
hashSnapshot() {
|
hashSnapshot() {
|
||||||
return JSON.stringify(this.hash);
|
const { prev, ...asHash } = this.hash;
|
||||||
|
return JSON.stringify(asHash);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inject: ['appConfig'],
|
inject: ['appConfig'],
|
||||||
created() {
|
created() {
|
||||||
|
this.mobileMediaQuery = window.matchMedia('only screen and (max-width: 800px)');
|
||||||
|
this.mobileMediaQuery.addEventListener('change', this.updateMobile);
|
||||||
|
this.updateMobile();
|
||||||
|
|
||||||
//Set initial page
|
//Set initial page
|
||||||
let asInitHash = this.getBrowserHash();
|
this.setVarHash(this.validateRoute(this.getBrowserHash()));
|
||||||
if(!asInitHash.page) asInitHash.page = this.consts.default_page;
|
|
||||||
this.setVarHash(asInitHash);
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
//Catch browser hash change
|
//Catch browser hash change
|
||||||
window.addEventListener('hashchange', this.onBrowserHashChange);
|
window.addEventListener('hashchange', this.onBrowserHashChange);
|
||||||
window.addEventListener('resize', this.updateMobile);
|
|
||||||
this.updateMobile();
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
hashSnapshot(jNewHash, jOldHash) {
|
hashSnapshot(jNewHash, jOldHash) {
|
||||||
const asNewHash = JSON.parse(jNewHash);
|
const asNewHash = this.validateRoute(JSON.parse(jNewHash));
|
||||||
|
|
||||||
//Sync variable -> #hash
|
//Sync variable -> #hash
|
||||||
if(asNewHash != this.getBrowserHash()) {
|
if(asNewHash != this.getBrowserHash()) {
|
||||||
this.setBrowserHash(asNewHash.page, asNewHash.items);
|
this.setBrowserHash(asNewHash.page, asNewHash.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Store previous value
|
||||||
|
this.hash.prev = JSON.parse(jOldHash);
|
||||||
|
|
||||||
this.setPageTitle(asNewHash.page);
|
this.setPageTitle(asNewHash.page);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -61,7 +67,7 @@ export default {
|
|||||||
return this.mobile;
|
return this.mobile;
|
||||||
},
|
},
|
||||||
updateMobile() {
|
updateMobile() {
|
||||||
this.mobile = getComputedStyle(this.$refs.mobile).display !== 'none';
|
this.mobile = this.mobileMediaQuery.matches;
|
||||||
},
|
},
|
||||||
setPageTitle(sTitle) {
|
setPageTitle(sTitle) {
|
||||||
document.title = this.consts.title + ' - ' + sTitle.trim();
|
document.title = this.consts.title + ' - ' + sTitle.trim();
|
||||||
@@ -82,13 +88,22 @@ export default {
|
|||||||
},
|
},
|
||||||
setBrowserHash(sPage = '', asItems = []) {
|
setBrowserHash(sPage = '', asItems = []) {
|
||||||
if(typeof asItems == 'string' && asItems != '') asItems = [asItems];
|
if(typeof asItems == 'string' && asItems != '') asItems = [asItems];
|
||||||
const sItems = (asItems.length > 0)?(this.consts.hash_sep + asItems.join(this.consts.hash_sep)):'';
|
window.location.hash = this.getAnchor([sPage, ...asItems]);
|
||||||
window.location.hash = '#' + sPage + sItems;
|
},
|
||||||
|
getAnchor(asBreadCrumbs) {
|
||||||
|
return '#' + asBreadCrumbs.filter(Boolean).join(this.consts.hash_sep);
|
||||||
|
},
|
||||||
|
getPrevAnchor() {
|
||||||
|
return this.getAnchor([this.hash.prev.page, ...this.hash.prev.items]);
|
||||||
|
},
|
||||||
|
validateRoute(asHash) {
|
||||||
|
if(!Object.keys(aoRoutes).includes(asHash.page)) asHash.page = this.consts.default_page;
|
||||||
|
return asHash;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
window.removeEventListener('hashchange', this.onBrowserHashChange);
|
window.removeEventListener('hashchange', this.onBrowserHashChange);
|
||||||
window.removeEventListener('resize', this.updateMobile);
|
this.mobileMediaQuery.removeEventListener('change', this.updateMobile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -96,5 +111,4 @@ export default {
|
|||||||
<div id="main">
|
<div id="main">
|
||||||
<component :is="route" />
|
<component :is="route" />
|
||||||
</div>
|
</div>
|
||||||
<div id="mobile" ref="mobile"></div>
|
|
||||||
</template>
|
</template>
|
||||||
17
src/app.js
@@ -6,7 +6,7 @@ import User from '@scripts/user';
|
|||||||
import { createApp, reactive } from 'vue';
|
import { createApp, reactive } from 'vue';
|
||||||
|
|
||||||
//Main template
|
//Main template
|
||||||
import Spot from './Spot';
|
import App from './App';
|
||||||
|
|
||||||
//Style
|
//Style
|
||||||
import Css from '@styles/spot';
|
import Css from '@styles/spot';
|
||||||
@@ -22,15 +22,16 @@ const oApi = new Api({
|
|||||||
server: appConfig.consts.server,
|
server: appConfig.consts.server,
|
||||||
processPage: appConfig.consts.process_page,
|
processPage: appConfig.consts.process_page,
|
||||||
timezone: oUser.timezone,
|
timezone: oUser.timezone,
|
||||||
|
csrfToken: appConfig.consts.csrf_token,
|
||||||
errorCode: appConfig.consts.error,
|
errorCode: appConfig.consts.error,
|
||||||
lang: oLang
|
lang: oLang
|
||||||
});
|
});
|
||||||
|
|
||||||
//Mount app
|
//Mount app
|
||||||
const oSpot = createApp(Spot);
|
const oApp = createApp(App);
|
||||||
oSpot.provide('appConfig', appConfig);
|
oApp.provide('appConfig', appConfig);
|
||||||
oSpot.provide('api', oApi);
|
oApp.provide('api', oApi);
|
||||||
oSpot.provide('lang', oLang);
|
oApp.provide('lang', oLang);
|
||||||
oSpot.provide('projects', oProjects);
|
oApp.provide('projects', oProjects);
|
||||||
oSpot.provide('user', oUser);
|
oApp.provide('user', oUser);
|
||||||
oSpot.mount('#container');
|
oApp.mount('#container');
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default {
|
|||||||
SpotButton,
|
SpotButton,
|
||||||
AdminInput
|
AdminInput
|
||||||
},
|
},
|
||||||
inject: ['api', 'lang'],
|
inject: ['api', 'lang', 'getPrevAnchor'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
elems: {},
|
elems: {},
|
||||||
@@ -50,7 +50,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
createElem(sType) {
|
createElem(sType) {
|
||||||
this.api.get('admin_create', {type: sType})
|
this.api.post('admin_create', {type: sType})
|
||||||
.then((aoNewElemTypes) => {
|
.then((aoNewElemTypes) => {
|
||||||
for(const [sType, aoNewElems] of Object.entries(aoNewElemTypes)) {
|
for(const [sType, aoNewElems] of Object.entries(aoNewElemTypes)) {
|
||||||
for(const [iKey, oNewElem] of Object.entries(aoNewElems)) {
|
for(const [iKey, oNewElem] of Object.entries(aoNewElems)) {
|
||||||
@@ -68,7 +68,7 @@ export default {
|
|||||||
id: oElem.id
|
id: oElem.id
|
||||||
};
|
};
|
||||||
|
|
||||||
this.api.get('admin_delete', asInputs)
|
this.api.post('admin_delete', asInputs)
|
||||||
.then((asData) => {
|
.then((asData) => {
|
||||||
delete this.elems[asInputs.type][asInputs.id];
|
delete this.elems[asInputs.type][asInputs.id];
|
||||||
this.addFeedback('success', this.l('admin.delete_success'), asInputs);
|
this.addFeedback('success', this.l('admin.delete_success'), asInputs);
|
||||||
@@ -90,7 +90,7 @@ export default {
|
|||||||
value: sNewVal
|
value: sNewVal
|
||||||
};
|
};
|
||||||
|
|
||||||
this.api.get('admin_set', asInputs)
|
this.api.post('admin_set', asInputs)
|
||||||
.then((asData) => {
|
.then((asData) => {
|
||||||
this.elems[oElem.type][oElem.id][oEvent.target.name] = sNewVal;
|
this.elems[oElem.type][oElem.id][oEvent.target.name] = sNewVal;
|
||||||
this.addFeedback('success', this.l('admin.save_success'), asInputs);
|
this.addFeedback('success', this.l('admin.save_success'), asInputs);
|
||||||
@@ -106,7 +106,7 @@ export default {
|
|||||||
this.saveTimer = setTimeout(() => {this.updateElem(oElem, oEvent);}, 2000);
|
this.saveTimer = setTimeout(() => {this.updateElem(oElem, oEvent);}, 2000);
|
||||||
},
|
},
|
||||||
updateProject() {
|
updateProject() {
|
||||||
this.api.get('update_project')
|
this.api.post('update_project')
|
||||||
.then((asData, sMsg) => {this.addFeedback('success', sMsg, {'update':'project'});})
|
.then((asData, sMsg) => {this.addFeedback('success', sMsg, {'update':'project'});})
|
||||||
.catch((sMsg) => {this.addFeedback('error', sMsg, {'update':'project'});});
|
.catch((sMsg) => {this.addFeedback('error', sMsg, {'update':'project'});});
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div id="admin">
|
<div id="admin">
|
||||||
<a name="back" class="button" href="#project"><SpotIcon :icon="'back'" :text="l('action.back')" /></a>
|
<a name="back" class="button" :href="getPrevAnchor()"><SpotIcon :icon="'back'" :text="l('action.back')" /></a>
|
||||||
<h1>{{ l('project.plural') }}</h1>
|
<h1>{{ l('project.plural') }}</h1>
|
||||||
<div>
|
<div>
|
||||||
<table>
|
<table>
|
||||||
|
|||||||
@@ -1,59 +1,85 @@
|
|||||||
<script>
|
<script>
|
||||||
import 'maplibre-gl/dist/maplibre-gl.css';
|
import 'maplibre-gl/dist/maplibre-gl.css';
|
||||||
import { Map, Marker, LngLatBounds, LngLat, Popup } from 'maplibre-gl';
|
import { Map, Marker, LngLatBounds, LngLat, Popup, ScaleControl, NavigationControl } from 'maplibre-gl';
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import Simplebar from 'simplebar-vue';
|
|
||||||
|
|
||||||
import Lightbox from '@scripts/lightbox';
|
import Lightbox from '@scripts/lightbox';
|
||||||
import { getOuterWidth } from '@scripts/common';
|
|
||||||
|
|
||||||
import SpotIcon from '@components/spotIcon';
|
import SpotIcon from '@components/spotIcon';
|
||||||
import SpotIconStack from '@components/spotIconStack';
|
import SpotIconStack from '@components/spotIconStack';
|
||||||
import ProjectPost from '@components/projectPost';
|
|
||||||
import ProjectPopup from '@components/projectPopup';
|
import ProjectPopup from '@components/projectPopup';
|
||||||
import ProjectNewsletter from '@components/projectNewsletter';
|
import ProjectFeed from '@components/projectFeed';
|
||||||
|
import ProjectSettings from '@components/projectSettings';
|
||||||
|
|
||||||
|
class GroupedScaleControl {
|
||||||
|
constructor(options) {
|
||||||
|
this.scale = new ScaleControl(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAdd(map) {
|
||||||
|
this.container = document.createElement('div');
|
||||||
|
this.container.className = 'maplibregl-ctrl maplibregl-ctrl-group';
|
||||||
|
|
||||||
|
const scaleElement = this.scale.onAdd(map);
|
||||||
|
scaleElement.classList.remove('maplibregl-ctrl');
|
||||||
|
this.container.appendChild(scaleElement);
|
||||||
|
|
||||||
|
return this.container;
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove() {
|
||||||
|
this.scale.onRemove();
|
||||||
|
this.container.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SpotIcon,
|
SpotIcon,
|
||||||
ProjectPost,
|
ProjectFeed,
|
||||||
ProjectNewsletter,
|
ProjectSettings
|
||||||
Simplebar
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
feed: {loading:false, updatable:true, outOfData:false, refIdFirst:0, refIdLast:0, firstChunk:true},
|
panels: {
|
||||||
refreshRate: 60,
|
leftOpen: false,
|
||||||
lastUpdate: { unix_time: 0, relative_time: '', formatted_time: ''},
|
rightOpen: false
|
||||||
feedPanelOpen: false,
|
},
|
||||||
settingsPanelOpen: false,
|
feed: null,
|
||||||
|
settings: null,
|
||||||
track: null,
|
track: null,
|
||||||
markers: [],
|
markers: [],
|
||||||
markerProps: {
|
markerProps: {
|
||||||
|
project: {mainClasses: 'project', iconMain: 'marker', iconSub: 'project'},
|
||||||
image: {mainClasses: 'media', iconMain: 'marker', iconSub: 'image'},
|
image: {mainClasses: 'media', iconMain: 'marker', iconSub: 'image'},
|
||||||
video: {mainClasses: 'media', iconMain: 'marker', iconSub: 'video'},
|
video: {mainClasses: 'media', iconMain: 'marker', iconSub: 'video'},
|
||||||
message: {mainClasses: 'message', iconMain: 'marker', iconSub: 'footprint', iconSubTransform: 'rotate-270'}
|
message: {mainClasses: 'message', iconMain: 'marker', iconSub: 'footprint', iconSubTransform: 'rotate-270'}
|
||||||
},
|
},
|
||||||
currProject: {},
|
project: null,
|
||||||
modeHisto: false,
|
modeHisto: null,
|
||||||
posts: [],
|
baseMaps: [],
|
||||||
baseMaps: {},
|
|
||||||
baseMap: null,
|
baseMap: null,
|
||||||
map: null,
|
map: null,
|
||||||
|
mapInitializing: false,
|
||||||
|
markerHeight: 32, //FIXME
|
||||||
|
mapPadding: 16 + 32, //1rem + marker height
|
||||||
|
maxZoom: 15,
|
||||||
|
initialPitch: 45,
|
||||||
lightbox: null,
|
lightbox: null,
|
||||||
hikes: {
|
hikes: {
|
||||||
colors:{'main':'#00ff78', 'off-track':'#0000ff', 'hitchhiking':'#FF7814'},
|
colors: {},
|
||||||
width: 4
|
width: null
|
||||||
},
|
},
|
||||||
popup: {content: null, element: null}
|
popup: {content: null, element: null},
|
||||||
|
overview: {id: 0, codename:'overview', name: this.lang.get('project.overview')},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
projectClasses() {
|
projectOptions() {
|
||||||
return [
|
return [
|
||||||
this.feedPanelOpen?'with-feed':'',
|
this.overview,
|
||||||
this.settingsPanelOpen?'with-settings':''
|
...Object.values(this.projects)
|
||||||
].filter(n => n).join(' ');
|
];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -63,10 +89,10 @@ export default {
|
|||||||
if(sNewBaseMap && this.map.getLayer(sNewBaseMap)) this.map.setLayoutProperty(sNewBaseMap, 'visibility', 'visible');
|
if(sNewBaseMap && this.map.getLayer(sNewBaseMap)) this.map.setLayoutProperty(sNewBaseMap, 'visibility', 'visible');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'hash.items.0'(newProjectCodename, oldProjectCodename) {
|
'hash.items.0'(newProjectCodename, oldProjectCodename) { //hash.items.0 = Project Code Name
|
||||||
if(newProjectCodename && newProjectCodename != oldProjectCodename) {
|
if(newProjectCodename != oldProjectCodename) {
|
||||||
this.hash.items = [newProjectCodename];
|
this.hash.items = [newProjectCodename]; //Force removal of direct link
|
||||||
this.toggleSettingsPanel(false, 'none');
|
this.settings.toggle(false, 0);
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,58 +109,72 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: ['api', 'lang', 'hash', 'projects', 'user', 'consts', 'isMobile'],
|
inject: ['api', 'lang', 'hash', 'projects', 'user', 'consts', 'isMobile'],
|
||||||
beforeMount() {
|
|
||||||
if(this.hash.items.length == 0) this.hash.items[0] = this.projects.getDefaultCodeName();
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.init();
|
//Starts default project init() through watcher
|
||||||
|
if(this.hash.items.length == 0) {
|
||||||
|
this.hash.items[0] = (this.projects.getDefaultProject().mode == this.consts.modes.blog)?this.projects.getDefaultCodeName():this.overview.codename;
|
||||||
|
}
|
||||||
|
else this.init();
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.quit();
|
this.quit();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async init() {
|
async init() {
|
||||||
this.initProject();
|
|
||||||
this.initLightbox();
|
this.initLightbox();
|
||||||
|
this.hikes.colors = {
|
||||||
|
'main': this.getStyleProperty('--track-main'),
|
||||||
|
'off-track': this.getStyleProperty('--track-off-track'),
|
||||||
|
'hitchhiking': this.getStyleProperty('--track-hitchhiking')
|
||||||
|
};
|
||||||
|
this.hikes.width = parseFloat(this.getStyleProperty('--track-width'));
|
||||||
|
|
||||||
await Promise.all([
|
//Reset values
|
||||||
this.initFeed(),
|
this.track = null;
|
||||||
this.initMap()
|
this.project = null;
|
||||||
]);
|
this.removeMapContent();
|
||||||
|
|
||||||
//Direct link post action
|
//Build Map
|
||||||
if(this.hash.items.length == 3) await this.findPost(this.hash.items[1], this.hash.items[2]);
|
this.mapInitializing = true;
|
||||||
|
if(this.hash.items[0] && this.projects[this.hash.items[0]]) await this.initProject(this.hash.items[0]);
|
||||||
|
else await this.initOverview();
|
||||||
|
this.mapInitializing = false;
|
||||||
},
|
},
|
||||||
quit() {
|
quit() {
|
||||||
this.lightbox.end();
|
this.lightbox.end(true);
|
||||||
this.$refs.feedSimpleBar.scrollElement.removeEventListener('scroll', this.onFeedScroll);
|
this.lightbox = null;
|
||||||
this.setFeedUpdateTimer(-1);
|
this.removeMap();
|
||||||
this.map.remove();
|
|
||||||
},
|
},
|
||||||
initProject() {
|
async initOverview() {
|
||||||
this.currProject = this.projects[this.hash.items[0]];
|
this.modeHisto = true;
|
||||||
this.modeHisto = (this.currProject.mode == this.consts.modes.histo);
|
this.hash.items = [this.overview.codename];
|
||||||
this.feed = {loading:false, updatable:true, outOfData:false, refIdFirst:0, refIdLast:0, firstChunk:true};
|
this.feed.toggle(false, 0);
|
||||||
this.posts = [];
|
|
||||||
//this.baseMap = null;
|
await this.initOverviewMap();
|
||||||
this.baseMaps = {};
|
},
|
||||||
|
async initProject(sProjectCodeName) {
|
||||||
|
this.project = this.projects[sProjectCodeName];
|
||||||
|
this.modeHisto = (this.project.mode == this.consts.modes.histo);
|
||||||
|
|
||||||
|
await this.$nextTick();
|
||||||
|
const pMapReady = this.initProjectMap();
|
||||||
|
await this.feed.init(pMapReady);
|
||||||
},
|
},
|
||||||
initLightbox() {
|
initLightbox() {
|
||||||
if(!this.lightbox) {
|
if(!this.lightbox) {
|
||||||
this.lightbox = new Lightbox({
|
this.lightbox = new Lightbox({
|
||||||
alwaysShowNavOnTouchDevices: true,
|
alwaysShowNavOnTouchDevices: true,
|
||||||
albumLabel: 'Media %1 / %2',
|
|
||||||
fadeDuration: 300,
|
fadeDuration: 300,
|
||||||
imageFadeDuration: 400,
|
imageFadeDuration: 400,
|
||||||
positionFromTop: 0,
|
positionFromTop: 0,
|
||||||
resizeDuration: 400,
|
resizeDuration: 400,
|
||||||
hasVideo: true,
|
hasVideo: true,
|
||||||
onMediaChange: async (oMedia) => {
|
onMediaChange: async (oMedia) => {
|
||||||
this.hash.items = [this.currProject.codename, 'media', oMedia.id];
|
this.hash.items = [this.project.codename, 'media', oMedia.id];
|
||||||
if(oMedia.set == 'post-medias') {
|
if(oMedia.set == 'post-medias') {
|
||||||
this.goToPost('media', oMedia.id)?.panMapToMarker();
|
(await this.feed.goToPost('media', oMedia.id))?.panMapToMarker();
|
||||||
if(!this.lightbox.hasMediaAfterCurrent()) {
|
if(!this.lightbox.hasMediaAfterCurrent()) {
|
||||||
await this.getNextFeed();
|
await this.feed.getNextFeed();
|
||||||
await this.$nextTick();
|
await this.$nextTick();
|
||||||
this.lightbox.refreshAlbum();
|
this.lightbox.refreshAlbum();
|
||||||
}
|
}
|
||||||
@@ -144,102 +184,113 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async initFeed() {
|
async initProjectMap() {
|
||||||
//Simplebar event
|
|
||||||
this.$refs.feedSimpleBar?.scrollElement.addEventListener('scroll', this.onFeedScroll);
|
|
||||||
|
|
||||||
//Mobile Touchscreen Events
|
|
||||||
//TODO
|
|
||||||
|
|
||||||
this.toggleFeedPanel(!this.isMobile(), 'none');
|
|
||||||
|
|
||||||
//Add post Event handling
|
|
||||||
//TODO
|
|
||||||
|
|
||||||
//Get first posts batch
|
|
||||||
await this.getNextFeed();
|
|
||||||
this.$refs.feedSimpleBar.scrollElement.scrollTop = 0;
|
|
||||||
|
|
||||||
//Start auto-update
|
|
||||||
if(!this.modeHisto) this.setFeedUpdateTimer(this.refreshRate);
|
|
||||||
},
|
|
||||||
async initMap() {
|
|
||||||
//Start async calls
|
|
||||||
[
|
[
|
||||||
{
|
{maps: this.baseMaps, markers: this.markers},
|
||||||
maps: this.baseMaps,
|
|
||||||
markers: this.markers,
|
|
||||||
last_update: this.lastUpdate
|
|
||||||
},
|
|
||||||
this.track
|
this.track
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.api.get('markers', {id_project: this.currProject.id}),
|
this.api.get('markers', {id_project: this.project.id}),
|
||||||
this.api.get('geojson', {id_project: this.currProject.id})
|
this.api.get('geojson', {id_project: this.project.id})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//Build Map
|
await this.initMap();
|
||||||
if(this.map) this.map.remove();
|
},
|
||||||
this.map = new Map({
|
async initOverviewMap() {
|
||||||
container: 'map',
|
this.baseMaps = this.consts.default_maps;
|
||||||
bounds: this.getInitialMapBounds(),
|
this.markers = Object.values(this.projects).map((asProject) => ({
|
||||||
fitBoundsOptions: {
|
type: 'project',
|
||||||
padding: {
|
subtype: 'project',
|
||||||
top: 20,
|
...asProject,
|
||||||
bottom: 20,
|
opacityWhenCovered: 0.3
|
||||||
left: 20,
|
}));
|
||||||
right: 20 + (this.feedPanelOpen?(getOuterWidth(this.$refs.feed)):0)
|
|
||||||
},
|
|
||||||
animate: false,
|
|
||||||
maxZoom: 15
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
version: 8,
|
|
||||||
sources: {},
|
|
||||||
layers: []
|
|
||||||
},
|
|
||||||
attributionControl: false
|
|
||||||
});
|
|
||||||
|
|
||||||
//Get default basemap
|
await this.initMap();
|
||||||
this.baseMap = this.baseMaps.find((asBM) => asBM.default_map)?.codename ?? null;
|
},
|
||||||
|
async initMap() {
|
||||||
|
//Build map
|
||||||
|
if(!this.map) this.addMap();
|
||||||
|
this.updateMapPadding();
|
||||||
|
|
||||||
//Force wait for load event
|
//Force wait for load event
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
if(this.map.loaded()) resolve();
|
if(this.map.isStyleLoaded()) resolve();
|
||||||
else this.map.once('load', resolve);
|
else this.map.once('load', resolve);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Base maps (raster tiles)
|
this.map.resize();
|
||||||
for(const asBaseMap of this.baseMaps) {
|
this.setInitialProjectCamera();
|
||||||
this.map.addSource(asBaseMap.codename, {
|
|
||||||
type: 'raster',
|
|
||||||
tiles: [asBaseMap.pattern],
|
|
||||||
tileSize: asBaseMap.tile_size
|
|
||||||
});
|
|
||||||
this.map.addLayer({
|
|
||||||
id: asBaseMap.codename,
|
|
||||||
type: 'raster',
|
|
||||||
source: asBaseMap.codename,
|
|
||||||
'layout': {'visibility': asBaseMap.codename == this.baseMap ? 'visible' : 'none'},
|
|
||||||
minZoom: asBaseMap.min_zoom,
|
|
||||||
maxZoom: asBaseMap.max_zoom
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add track
|
//Add content: Base Maps, Tracks, Markers
|
||||||
this.addTrack(this.track);
|
this.addMapContent();
|
||||||
|
|
||||||
//Add Markers
|
|
||||||
this.markers.forEach(oMarker => this.addMarker(oMarker));
|
|
||||||
|
|
||||||
//Force wait for idle event
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
if(this.map.loaded() && this.map.areTilesLoaded()) resolve();
|
if(this.map.loaded() && this.map.areTilesLoaded()) resolve();
|
||||||
else this.map.once('idle', resolve);
|
else this.map.once('idle', resolve);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addTrack(oTrack, bCenter=false) {
|
addMap() {
|
||||||
this.track = oTrack;
|
this.map = new Map({
|
||||||
|
container: 'map',
|
||||||
|
aroundCenter: true,
|
||||||
|
style: {
|
||||||
|
version: 8,
|
||||||
|
projection: {type: 'globe'},
|
||||||
|
sky: {
|
||||||
|
'sky-color': this.getStyleProperty('--space'),
|
||||||
|
'horizon-color': this.getStyleProperty('--horizon'),
|
||||||
|
'sky-horizon-blend': 0.35,
|
||||||
|
'atmosphere-blend': 0.8
|
||||||
|
},
|
||||||
|
sources: {},
|
||||||
|
layers: []
|
||||||
|
},
|
||||||
|
attributionControl: false
|
||||||
|
});
|
||||||
|
this.map.addControl(new GroupedScaleControl({unit: 'metric'}), 'bottom-right');
|
||||||
|
this.map.addControl(new NavigationControl({showZoom: false, visualizePitch: true}), 'bottom-right');
|
||||||
|
},
|
||||||
|
removeMap() {
|
||||||
|
this.removeMapContent();
|
||||||
|
this.map?.remove();
|
||||||
|
this.map = null;
|
||||||
|
},
|
||||||
|
addMapContent() {
|
||||||
|
this.baseMaps.forEach(this.addBaseMap);
|
||||||
|
this.addTrack();
|
||||||
|
this.markers.forEach(this.addMarker);
|
||||||
|
},
|
||||||
|
removeMapContent() {
|
||||||
|
if(!this.map) return;
|
||||||
|
|
||||||
|
this.closePopup();
|
||||||
|
this.removeTrack();
|
||||||
|
this.markers.forEach(this.removeMarker);
|
||||||
|
this.baseMaps.forEach(this.removeBaseMap);
|
||||||
|
},
|
||||||
|
addBaseMap(asBaseMap) {
|
||||||
|
if(asBaseMap.default_map) this.baseMap = asBaseMap.codename;
|
||||||
|
if(this.map.getSource(asBaseMap.codename) && this.map.getLayer(asBaseMap.codename)) return;
|
||||||
|
this.map.addSource(asBaseMap.codename, {
|
||||||
|
type: 'raster',
|
||||||
|
tiles: [asBaseMap.pattern],
|
||||||
|
tileSize: asBaseMap.tile_size
|
||||||
|
});
|
||||||
|
this.map.addLayer({
|
||||||
|
id: asBaseMap.codename,
|
||||||
|
type: 'raster',
|
||||||
|
source: asBaseMap.codename,
|
||||||
|
'layout': {'visibility': asBaseMap.default_map?'visible':'none'},
|
||||||
|
minZoom: asBaseMap.min_zoom,
|
||||||
|
maxZoom: asBaseMap.max_zoom
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeBaseMap(asBaseMap) {
|
||||||
|
if(this.map.getLayer(asBaseMap.codename)) this.map.removeLayer(asBaseMap.codename);
|
||||||
|
if(this.map.getSource(asBaseMap.codename)) this.map.removeSource(asBaseMap.codename);
|
||||||
|
},
|
||||||
|
addTrack() {
|
||||||
|
if(!this.track) return;
|
||||||
|
|
||||||
this.track.features.forEach((oFeature, iFeatureId) => {
|
this.track.features.forEach((oFeature, iFeatureId) => {
|
||||||
oFeature.properties.track_id = iFeatureId;
|
oFeature.properties.track_id = iFeatureId;
|
||||||
});
|
});
|
||||||
@@ -278,44 +329,97 @@ export default {
|
|||||||
'source': 'track',
|
'source': 'track',
|
||||||
'paint': {
|
'paint': {
|
||||||
'line-opacity': 0,
|
'line-opacity': 0,
|
||||||
'line-width': this.hikes.width + 20
|
'line-width': this.hikes.width + this.mapPadding
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.map.on('click', 'track-hitbox', this.openTrackPopup);
|
this.map.on('click', 'track-hitbox', this.openTrackPopup);
|
||||||
this.map.on('mouseenter', 'track-hitbox', () => {this.map.getCanvas().style.cursor = 'pointer';});
|
this.map.on('mouseenter', 'track-hitbox', this.onTrackHover);
|
||||||
this.map.on('mouseleave', 'track-hitbox', () => {this.map.getCanvas().style.cursor = '';});
|
this.map.on('mouseleave', 'track-hitbox', this.onTrackHover);
|
||||||
|
},
|
||||||
|
removeTrack() {
|
||||||
|
//Over clickable track
|
||||||
|
if(this.map.getLayer('track-hitbox')) {
|
||||||
|
this.map.off('click', 'track-hitbox', this.openTrackPopup);
|
||||||
|
this.map.off('mouseenter', 'track-hitbox', this.onTrackHover);
|
||||||
|
this.map.off('mouseleave', 'track-hitbox', this.onTrackHover);
|
||||||
|
this.map.removeLayer('track-hitbox');
|
||||||
|
}
|
||||||
|
|
||||||
|
//Actual track
|
||||||
|
if(this.map.getLayer('track')) this.map.removeLayer('track');
|
||||||
|
|
||||||
|
//Track source
|
||||||
|
if(this.map.getSource('track')) this.map.removeSource('track');
|
||||||
|
},
|
||||||
|
addMarker(oMarker) {
|
||||||
|
const $Marker = document.createElement('div');
|
||||||
|
oMarker.app = createApp(SpotIconStack, this.markerProps[oMarker.subtype]);
|
||||||
|
oMarker.app.mount($Marker);
|
||||||
|
|
||||||
|
oMarker.marker = new Marker({element: $Marker, anchor: 'bottom', opacityWhenCovered: oMarker.opacityWhenCovered ?? 0})
|
||||||
|
.setLngLat([oMarker.longitude, oMarker.latitude])
|
||||||
|
.addTo(this.map);
|
||||||
|
|
||||||
|
const $MarkerElement = oMarker.marker.getElement();
|
||||||
|
$MarkerElement.addEventListener('click', (oEvent) => {this.onMarkerClick(oEvent, oMarker);});
|
||||||
|
$MarkerElement.addEventListener('mouseenter', (oEvent) => {this.onMarkerHover(oEvent, oMarker);});
|
||||||
|
$MarkerElement.addEventListener('mouseleave', (oEvent) => {this.onMarkerHover(oEvent, oMarker);});
|
||||||
|
},
|
||||||
|
removeMarker(oMarker) {
|
||||||
|
if(oMarker.app) {
|
||||||
|
oMarker.app.unmount();
|
||||||
|
delete oMarker.app;
|
||||||
|
}
|
||||||
|
if(oMarker.marker) {
|
||||||
|
oMarker.marker.remove();
|
||||||
|
delete oMarker.marker;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onTrackHover(oEvent) {
|
||||||
|
this.map.getCanvas().style.cursor = (oEvent.type == 'mouseenter')?'pointer':'';
|
||||||
|
},
|
||||||
|
onMarkerClick(oEvent, oMarker) {
|
||||||
|
oEvent.preventDefault();
|
||||||
|
oEvent.stopPropagation();
|
||||||
|
switch (oMarker.type) {
|
||||||
|
case 'project':
|
||||||
|
this.hash.items = [oMarker.codename];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.openMarkerPopup(oMarker.id, oMarker.type);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onMarkerHover(oEvent, oMarker) {
|
||||||
|
switch (oMarker.type) {
|
||||||
|
case 'project':
|
||||||
|
if(oEvent.type == 'mouseenter') this.openProjectPopup(oMarker);
|
||||||
|
else this.closePopup();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openProjectPopup(oProject) {
|
||||||
|
this.openPopup({
|
||||||
|
lnglat: [oProject.longitude, oProject.latitude],
|
||||||
|
options: oProject,
|
||||||
|
offset: [0, -1 * this.markerHeight * this.getStyleProperty('--zoom-scale')]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openMarkerPopup(iMarkerId, sMarkerType) {
|
||||||
|
let oMarker = this.markers.find((oCandidate) => oCandidate.id == iMarkerId && oCandidate.type == sMarkerType);
|
||||||
|
this.openPopup({
|
||||||
|
lnglat: [oMarker.longitude, oMarker.latitude],
|
||||||
|
options: oMarker,
|
||||||
|
offset: [0, -1 * this.markerHeight]
|
||||||
|
});
|
||||||
},
|
},
|
||||||
openTrackPopup(oEvent) {
|
openTrackPopup(oEvent) {
|
||||||
this.closePopup();
|
|
||||||
this.openPopup({
|
this.openPopup({
|
||||||
lnglat: oEvent.lngLat,
|
lnglat: oEvent.lngLat,
|
||||||
options: this.projects.getTrackInfo(oEvent.features[0], this.track, this.lang),
|
options: this.projects.getTrackInfo(oEvent.features[0], this.track, this.lang),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addMarker(oMarker) {
|
|
||||||
const $Marker = document.createElement('div');
|
|
||||||
createApp(SpotIconStack, this.markerProps[oMarker.subtype]).mount($Marker);
|
|
||||||
|
|
||||||
new Marker({element: $Marker, anchor: 'bottom'})
|
|
||||||
.setLngLat([oMarker.longitude, oMarker.latitude])
|
|
||||||
.addTo(this.map)
|
|
||||||
.getElement()
|
|
||||||
.addEventListener('click', (oEvent) => {
|
|
||||||
oEvent.preventDefault();
|
|
||||||
oEvent.stopPropagation();
|
|
||||||
this.openMarkerPopup(oMarker.id, oMarker.type);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
openMarkerPopup(iMarkerId, sMarkerType) {
|
|
||||||
this.closePopup();
|
|
||||||
let oMarker = this.markers.find((oCandidate) => oCandidate.id == iMarkerId && oCandidate.type == sMarkerType);
|
|
||||||
this.openPopup({
|
|
||||||
lnglat: [oMarker.longitude, oMarker.latitude],
|
|
||||||
options: oMarker,
|
|
||||||
offset: [0, -32] //FIXME
|
|
||||||
});
|
|
||||||
},
|
|
||||||
openPopup({lnglat, options={}, offset=[0, 0]} = {}) {
|
openPopup({lnglat, options={}, offset=[0, 0]} = {}) {
|
||||||
|
this.closePopup();
|
||||||
const $Popup = document.createElement('div');
|
const $Popup = document.createElement('div');
|
||||||
this.popup.element = new Popup({
|
this.popup.element = new Popup({
|
||||||
anchor: 'bottom',
|
anchor: 'bottom',
|
||||||
@@ -328,11 +432,13 @@ export default {
|
|||||||
|
|
||||||
this.popup.content = createApp(ProjectPopup, {
|
this.popup.content = createApp(ProjectPopup, {
|
||||||
options: options,
|
options: options,
|
||||||
project: this.currProject
|
project: this.project,
|
||||||
|
hikes: this.hikes
|
||||||
});
|
});
|
||||||
this.popup.content
|
this.popup.content
|
||||||
.provide('lang', this.lang)
|
.provide('lang', this.lang)
|
||||||
.provide('consts', this.consts)
|
.provide('consts', this.consts)
|
||||||
|
.provide('isMobile', this.isMobile)
|
||||||
.mount($Popup);
|
.mount($Popup);
|
||||||
},
|
},
|
||||||
closePopup() {
|
closePopup() {
|
||||||
@@ -345,10 +451,8 @@ export default {
|
|||||||
this.popup.element = null;
|
this.popup.element = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getInitialMapBounds() {
|
setInitialProjectCamera() {
|
||||||
let oBounds = new LngLatBounds();
|
|
||||||
let oHashMarker;
|
let oHashMarker;
|
||||||
|
|
||||||
if(this.hash.items.length == 3) {
|
if(this.hash.items.length == 3) {
|
||||||
oHashMarker = this.markers.find((oMarker) => (
|
oHashMarker = this.markers.find((oMarker) => (
|
||||||
oMarker.type == this.hash.items[1] &&
|
oMarker.type == this.hash.items[1] &&
|
||||||
@@ -358,118 +462,106 @@ export default {
|
|||||||
)) || null;
|
)) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(oHashMarker) { //Direct link to marker
|
let oLastMarker = this.markers.at(-1);
|
||||||
oBounds.extend(new LngLat(oHashMarker.longitude, oHashMarker.latitude));
|
|
||||||
|
//Overview map: Center on default project
|
||||||
|
if(!this.project) {
|
||||||
|
//Center on default project
|
||||||
|
const oDefaultProject = this.projects.getDefaultProject();
|
||||||
|
|
||||||
|
//Get Map / Canvas size
|
||||||
|
const $Canvas = this.map.getCanvas();
|
||||||
|
const oMapBounds = this.map.getContainer().getBoundingClientRect();
|
||||||
|
|
||||||
|
//Adapt zoom to see whole planet
|
||||||
|
const iTargetRadius = Math.max(1, Math.min(oMapBounds.width || $Canvas.clientWidth, oMapBounds.height || $Canvas.clientHeight) / 2);
|
||||||
|
const iWorldSize = iTargetRadius * 2 * Math.PI * Math.cos(oDefaultProject.latitude * Math.PI / 180);
|
||||||
|
|
||||||
|
this.map.jumpTo({
|
||||||
|
center: new LngLat(oDefaultProject.longitude, oDefaultProject.latitude),
|
||||||
|
zoom: Math.log2(iWorldSize / this.map.transform.tileSize),
|
||||||
|
pitch: 0,
|
||||||
|
bearing: 0
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else if( //Blog Mode: Fit to last message
|
//Direct link to marker
|
||||||
this.currProject.mode == this.consts.modes.blog &&
|
else if(oHashMarker) {
|
||||||
this.markers.length > 0
|
this.map.jumpTo({
|
||||||
) {
|
center: new LngLat(oHashMarker.longitude, oHashMarker.latitude),
|
||||||
let oLastMsg = this.markers.at(-1);
|
zoom: 13,
|
||||||
oBounds.extend(new LngLat(oLastMsg.longitude, oLastMsg.latitude));
|
pitch: this.initialPitch
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else { //Pre/Histo Mode: Fit to track
|
//Blog Mode: Fit to last marker
|
||||||
|
else if(this.project.mode == this.consts.modes.blog && oLastMarker) {
|
||||||
|
this.map.jumpTo({
|
||||||
|
center: new LngLat(oLastMarker.longitude, oLastMarker.latitude),
|
||||||
|
zoom: this.maxZoom,
|
||||||
|
pitch: this.initialPitch,
|
||||||
|
bearing: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//Pre Mode, Histo Mode, Blog Mode without markers or missing direct link marker: Fit to track
|
||||||
|
else {
|
||||||
|
let oBounds = new LngLatBounds();
|
||||||
|
const aoTrackCoordinates = [];
|
||||||
for(const iFeatureId in this.track.features) {
|
for(const iFeatureId in this.track.features) {
|
||||||
oBounds = this.track.features[iFeatureId].geometry.coordinates.reduce(
|
oBounds = this.track.features[iFeatureId].geometry.coordinates.reduce(
|
||||||
(bounds, coord) => {
|
(bounds, coord) => {
|
||||||
|
aoTrackCoordinates.push(coord);
|
||||||
return bounds.extend(coord);
|
return bounds.extend(coord);
|
||||||
},
|
},
|
||||||
oBounds
|
oBounds
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return oBounds;
|
this.map.fitBounds(oBounds, {
|
||||||
},
|
padding: this.mapPadding,
|
||||||
|
animate: false,
|
||||||
|
maxZoom: this.maxZoom,
|
||||||
|
pitch: this.initialPitch,
|
||||||
|
bearing: 0
|
||||||
|
});
|
||||||
|
|
||||||
async findPost(sPostType, iPostId) {
|
this.fixPitchedCameraCenter(aoTrackCoordinates);
|
||||||
let vPost = this.goToPost(sPostType, iPostId);
|
|
||||||
if(vPost) {
|
|
||||||
await vPost.executeMainAction(0);
|
|
||||||
return vPost;
|
|
||||||
}
|
|
||||||
else if(!this.feed.outOfData) {
|
|
||||||
await this.getNextFeed();
|
|
||||||
return this.findPost(sPostType, iPostId);
|
|
||||||
}
|
|
||||||
else console.log('Missing element ID "'+iPostId+'" of type "'+sPostType+'"');
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
goToPost(sPostType, iPostId) {
|
|
||||||
let bFound = false;
|
|
||||||
let avPosts = this.$refs.posts.filter((post) => {return post.postId == sPostType+'-'+iPostId;});
|
|
||||||
if(avPosts.length > 0) {
|
|
||||||
let vPost = avPosts[0];
|
|
||||||
this.$refs.feedSimpleBar.scrollElement.scrollTop += Math.round(
|
|
||||||
vPost.$el.getBoundingClientRect().top
|
|
||||||
+ window.pageYOffset
|
|
||||||
- parseFloat(getComputedStyle(this.$refs.feedSimpleBar.$el).paddingTop)
|
|
||||||
);
|
|
||||||
|
|
||||||
return vPost;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getNextFeed() {
|
fixPitchedCameraCenter(aoTrackCoordinates) {
|
||||||
if(!this.feed.outOfData && !this.feed.loading) {
|
//Project min/max coords (lat, lng) onto map rectangle corner points (x, y)
|
||||||
//Get next chunk
|
const oScreenBounds = aoTrackCoordinates.reduce((oBounds, coord) => {
|
||||||
this.feed.loading = true;
|
const oPoint = this.map.project(coord);
|
||||||
let aoData = await this.api.get('next_feed', {id_project: this.currProject.id, id: this.feed.refIdLast});
|
return {
|
||||||
let iPostCount = Object.keys(aoData.feed).length;
|
minX: Math.min(oBounds.minX, oPoint.x),
|
||||||
|
minY: Math.min(oBounds.minY, oPoint.y),
|
||||||
|
maxX: Math.max(oBounds.maxX, oPoint.x),
|
||||||
|
maxY: Math.max(oBounds.maxY, oPoint.y)
|
||||||
|
};
|
||||||
|
}, {
|
||||||
|
minX: Infinity,
|
||||||
|
minY: Infinity,
|
||||||
|
maxX: -Infinity,
|
||||||
|
maxY: -Infinity
|
||||||
|
});
|
||||||
|
|
||||||
//Update pointers
|
//Current Rectangle center
|
||||||
this.feed.outOfData = (iPostCount < this.consts.chunk_size);
|
const oTrackCenter = {
|
||||||
if(iPostCount > 0) {
|
x: (oScreenBounds.minX + oScreenBounds.maxX) / 2,
|
||||||
this.feed.refIdLast = aoData.ref_id_last;
|
y: (oScreenBounds.minY + oScreenBounds.maxY) / 2
|
||||||
if(this.feed.firstChunk) this.feed.refIdFirst = aoData.ref_id_first;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
//Add posts
|
//Convert back center point (x, y) to coords and Move map to the track center
|
||||||
this.posts.push(...aoData.feed);
|
this.map.jumpTo({
|
||||||
|
center: this.map.unproject([
|
||||||
this.feed.loading = false;
|
oTrackCenter.x,
|
||||||
this.feed.firstChunk = false;
|
oTrackCenter.y
|
||||||
}
|
])
|
||||||
|
});
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
onFeedScroll(oEvent) {
|
addNewMarkers(aoMarkers) { //FIXME Use its own marker update API
|
||||||
const box = oEvent.currentTarget
|
this.markers.push(...aoMarkers);
|
||||||
const content = box.querySelector('.simplebar-content')
|
aoMarkers.forEach(this.addMarker);
|
||||||
|
|
||||||
if ((box.scrollTop + box.clientHeight) / (content?.offsetHeight || 1) >= 0.8) this.getNextFeed();
|
|
||||||
},
|
|
||||||
setFeedUpdateTimer(iSeconds) {
|
|
||||||
if(typeof this.feedTimer != 'undefined') clearTimeout(this.feedTimer);
|
|
||||||
if(iSeconds >= 0) this.feedTimer = setTimeout(this.checkNewFeed, iSeconds * 1000);
|
|
||||||
},
|
|
||||||
async checkNewFeed() {
|
|
||||||
let aoData = await this.api.get('new_feed', {id_project: this.currProject.id, id: this.feed.refIdFirst});
|
|
||||||
const aoFeed = aoData.feed || [];
|
|
||||||
const aoMarkers = aoData.markers || [];
|
|
||||||
|
|
||||||
if(aoFeed.length > 0) {
|
|
||||||
//Update pointer
|
|
||||||
this.feed.refIdFirst = aoData.ref_id_first;
|
|
||||||
|
|
||||||
//Add new posts
|
|
||||||
this.posts.unshift(...aoFeed);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Add new Markers
|
|
||||||
if(aoMarkers.length > 0) {
|
|
||||||
this.markers.push(...aoMarkers);
|
|
||||||
aoMarkers.forEach(oMarker => this.addMarker(oMarker));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Message Last Update
|
|
||||||
this.lastUpdate = aoData.last_update;
|
|
||||||
|
|
||||||
//Reschedule
|
|
||||||
this.setFeedUpdateTimer(this.refreshRate);
|
|
||||||
},
|
},
|
||||||
panToBetweenPanels(oLngLat, iZoom, iAnimDuration=500) {
|
panToBetweenPanels(oLngLat, iZoom, iAnimDuration=500) {
|
||||||
const iXOffset = (this.settingsPanelOpen?getOuterWidth(this.$refs.settings):0) - (this.feedPanelOpen?getOuterWidth(this.$refs.feed):0);
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
if(!this.map) {
|
if(!this.map) {
|
||||||
resolve();
|
resolve();
|
||||||
@@ -479,158 +571,80 @@ export default {
|
|||||||
this.map.easeTo({
|
this.map.easeTo({
|
||||||
center: oLngLat,
|
center: oLngLat,
|
||||||
zoom: iZoom,
|
zoom: iZoom,
|
||||||
offset: [iXOffset / 2, 0],
|
padding: this.getMapPadding(),
|
||||||
duration: iAnimDuration
|
duration: iAnimDuration
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getMapPadding() {
|
||||||
|
let bIsMobile = this.isMobile();
|
||||||
|
return {
|
||||||
|
top: this.mapPadding,
|
||||||
|
bottom: this.mapPadding,
|
||||||
|
left: this.mapPadding + ((!bIsMobile && this.panels.leftOpen && this.settings)?this.settings.getWidth():0),
|
||||||
|
right: this.mapPadding + ((!bIsMobile && this.panels.rightOpen && this.feed)?this.feed.getWidth():0)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
updateMapPadding(iDuration=0) {
|
||||||
|
const asPadding = this.getMapPadding();
|
||||||
|
if(iDuration > 0) this.map.easeTo({padding: asPadding, duration: iDuration});
|
||||||
|
else this.map.jumpTo({padding: asPadding});
|
||||||
|
},
|
||||||
|
getStyleProperty(sProperty) {
|
||||||
|
return getComputedStyle(this.$el).getPropertyValue(sProperty).trim();
|
||||||
|
},
|
||||||
isMarkerVisible(oLngLat){
|
isMarkerVisible(oLngLat){
|
||||||
return !!this.map && this.map.getBounds().contains(oLngLat);
|
return !!this.map && this.map.getBounds().contains(oLngLat);
|
||||||
},
|
},
|
||||||
getGoogleMapsLink(asInfo) {
|
onPanelToggle(sPanel, bNewValue, iAnimDuration=500) {
|
||||||
return $('<a>', {
|
const sPanelKey = sPanel + 'Open';
|
||||||
href:'https://www.google.com/maps/place/'+asInfo.lat_dms+'+'+asInfo.lon_dms+'/@'+asInfo.latitude+','+asInfo.longitude+',10z',
|
let bOldValue = this.panels[sPanelKey];
|
||||||
title: this.lang.get('map.see_on_google'),
|
this.panels[sPanelKey] = bNewValue;
|
||||||
target: '_blank',
|
|
||||||
rel: 'noreferrer noopener'
|
|
||||||
}).text(asInfo.lat_dms+' '+asInfo.lon_dms);
|
|
||||||
},
|
|
||||||
toggleFeedPanel(bShow, sMapAction) {
|
|
||||||
let bOldValue = this.feedPanelOpen;
|
|
||||||
this.feedPanelOpen = (typeof bShow === 'object' || typeof bShow === 'undefined')?(!this.feedPanelOpen):bShow;
|
|
||||||
|
|
||||||
if(bOldValue != this.feedPanelOpen && !this.isMobile()) {
|
if(bOldValue != bNewValue) {
|
||||||
sMapAction = sMapAction || 'panTo';
|
//Adjust map center
|
||||||
switch(sMapAction) {
|
if(!this.isMobile() && this.map) this.updateMapPadding(iAnimDuration);
|
||||||
case 'none':
|
|
||||||
break;
|
//Open Close panels
|
||||||
case 'panTo':
|
this.$el.classList.toggle('with-'+sPanel+'-panel');
|
||||||
this.map.panBy(
|
|
||||||
[(this.feedPanelOpen?1:-1) * getOuterWidth(this.$refs.feed) / 2, 0],
|
|
||||||
{duration: 500}
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'panToInstant':
|
|
||||||
this.map.panBy([(this.feedPanelOpen?1:-1) * getOuterWidth(this.$refs.feed) / 2, 0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleSettingsPanel(bShow, sMapAction) {
|
setFeed(vPanel) {
|
||||||
let bOldValue = this.settingsPanelOpen;
|
this.feed = vPanel;
|
||||||
this.settingsPanelOpen = (typeof bShow === 'object' || typeof bShow === 'undefined')?(!this.settingsPanelOpen):bShow;
|
},
|
||||||
|
setSettings(vPanel) {
|
||||||
if(bOldValue != this.settingsPanelOpen && !this.isMobile()) {
|
this.settings = vPanel;
|
||||||
sMapAction = sMapAction || 'panTo';
|
|
||||||
switch(sMapAction) {
|
|
||||||
case 'none':
|
|
||||||
break;
|
|
||||||
case 'panTo':
|
|
||||||
this.map.panBy(
|
|
||||||
[(this.settingsPanelOpen?-1:1) * getOuterWidth(this.$refs.settings) / 2, 0],
|
|
||||||
{duration: 500}
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'panToInstant':
|
|
||||||
this.map.panBy([(this.settingsPanelOpen?-1:1) * getOuterWidth(this.$refs.settings) /2, 0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="projects" :class="projectClasses">
|
<div class="projects">
|
||||||
<div id="background"></div>
|
<div id="space"></div>
|
||||||
<div id="submap">
|
<div id="submap">
|
||||||
<div class="loader">
|
<div class="loader">
|
||||||
<SpotIcon :icon="'map'" :classes="'flicker'" width="fixed" />
|
<SpotIcon :icon="'map'" :classes="'flicker'" width="fixed" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
<div id="settings" class="map-container map-container-left" ref="settings">
|
<ProjectSettings
|
||||||
<div id="settings-panel" class="map-panel">
|
:ref="setSettings"
|
||||||
<div class="settings-header">
|
:projects="projectOptions"
|
||||||
<div class="logo"><img width="289" height="72" src="images/logo_black.png" alt="Spotty" /></div>
|
v-model:project-code-name="hash.items[0]"
|
||||||
<div id="last_update" v-if="this.currProject.mode == this.consts.modes.blog && lastUpdate.unix_time > 0">
|
:base-maps="baseMaps"
|
||||||
<p><span><img src="images/spot-logo-only.svg" alt="" /></span><abbr :title="lastUpdate.formatted_time">{{ lang.get('feed.last_update')+' '+lastUpdate.relative_time }}</abbr></p>
|
v-model:base-map="baseMap"
|
||||||
</div>
|
:map-initializing="mapInitializing"
|
||||||
</div>
|
:hikes="hikes"
|
||||||
<div class="settings-sections">
|
@toggle="(bIsOpen, iAnimDuration) => onPanelToggle('left', bIsOpen, iAnimDuration)"
|
||||||
<Simplebar id="settings-sections-scrollbox">
|
/>
|
||||||
<div class="settings-section">
|
<ProjectFeed
|
||||||
<h1><SpotIcon :icon="'project'" width="fixed" :text="lang.get('project.hikes')" /></h1>
|
:ref="setFeed"
|
||||||
<div class="settings-section-body">
|
:project="project"
|
||||||
<div class="radio" v-for="project in projects" :key="'project-'+project.id">
|
:mode-histo="modeHisto"
|
||||||
<input type="radio" :id="'project-'+project.id" :value="project.codename" v-model="$parent.hash.items[0]" />
|
@request-last-update="settings?.setLastUpdate"
|
||||||
<label :for="'project-'+project.id">
|
@new-markers="addNewMarkers"
|
||||||
<span>{{ project.name }}</span>
|
@toggle="(bIsOpen, iAnimDuration) => onPanelToggle('right', bIsOpen, iAnimDuration)"
|
||||||
<a class="download" :href="project.gpxfilepath" :download="project.codename + '.gpx'" :title="lang.get('track.download')" @click.stop="()=>{}">
|
/>
|
||||||
<SpotIcon :icon="'download'" margin="left" />
|
|
||||||
</a>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-section">
|
|
||||||
<h1><SpotIcon :icon="'map'" width="fixed" :text="lang.get('map.title')" /></h1>
|
|
||||||
<div class="settings-section-body">
|
|
||||||
<div class="radio" v-for="bm in baseMaps" :key="'map-'+bm.id_map">
|
|
||||||
<input type="radio" :id="'map-'+bm.id_map" :value="bm.codename" v-model="baseMap" />
|
|
||||||
<label :for="'map-'+bm.id_map">{{ lang.get('map.'+bm.codename) }}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-section newsletter">
|
|
||||||
<ProjectNewsletter />
|
|
||||||
</div>
|
|
||||||
<div class="settings-section admin" v-if="user.hasClearance(consts.clearances.admin)">
|
|
||||||
<h1><SpotIcon :icon="'admin'" width="fixed" :text="lang.get('admin.title')" /></h1>
|
|
||||||
<div class="admin-actions">
|
|
||||||
<a class="button" href="#admin"><SpotIcon :icon="'config'" :text="lang.get('admin.config')" /></a>
|
|
||||||
<a class="button" href="#upload"><SpotIcon :icon="'upload'" :text="lang.get('admin.upload')" /></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Simplebar>
|
|
||||||
</div>
|
|
||||||
<div class="settings-footer">
|
|
||||||
<a href="https://git.lutran.fr/franzz/spot" :title="lang.get('credits.git')" target="_blank" rel="noopener">
|
|
||||||
<SpotIcon :icon="'credits'" :text="lang.get('credits.project')" />
|
|
||||||
</a>
|
|
||||||
<span> {{ lang.get('credits.license') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div :class="'map-control map-control-icon settings-control map-control-'+(isMobile()?'bottom':'top')" @click="toggleSettingsPanel">
|
|
||||||
<SpotIcon :icon="settingsPanelOpen?'prev':'menu'" />
|
|
||||||
</div>
|
|
||||||
<div v-if="!isMobile()" id="legend" class="map-control settings-control map-control-bottom">
|
|
||||||
<div v-for="(color, hikeType) in hikes.colors" class="track">
|
|
||||||
<span class="line" :style="'background-color:'+color+'; height:'+hikes.width+'px;'"></span>
|
|
||||||
<span class="desc">{{ lang.get('track.'+hikeType) }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="title" :class="'map-control settings-control map-control-'+(isMobile()?'bottom':'top')">
|
|
||||||
<span>{{ currProject.name }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="feed" class="map-container map-container-right" ref="feed">
|
|
||||||
<Simplebar id="feed-panel" class="map-panel" ref="feedSimpleBar">
|
|
||||||
<div id="feed-header">
|
|
||||||
<ProjectPost v-if="modeHisto" :options="{type: 'archived', headerless: true}" />
|
|
||||||
<ProjectPost v-else :options="{type: 'poster', relative_time: lang.get('post.new_message')}" />
|
|
||||||
</div>
|
|
||||||
<div id="feed-posts">
|
|
||||||
<ProjectPost v-for="post in posts" :options="post" ref="posts" />
|
|
||||||
</div>
|
|
||||||
<div id="feed-footer" v-if="feed.loading">
|
|
||||||
<ProjectPost :options="{type: 'loading', headerless: true}" />
|
|
||||||
</div>
|
|
||||||
</Simplebar>
|
|
||||||
<div :class="'map-control map-control-icon feed-control map-control-'+(isMobile()?'bottom':'top')" @click="toggleFeedPanel">
|
|
||||||
<SpotIcon :icon="feedPanelOpen?'next':'post'" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
224
src/components/projectFeed.vue
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
<script>
|
||||||
|
import Simplebar from 'simplebar-vue';
|
||||||
|
|
||||||
|
import SpotIcon from '@components/spotIcon';
|
||||||
|
import ProjectPost from '@components/projectPost';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SpotIcon,
|
||||||
|
ProjectPost,
|
||||||
|
Simplebar
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
project: Object,
|
||||||
|
modeHisto: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
updatable: true,
|
||||||
|
outOfData: false,
|
||||||
|
refIdFirst: 0,
|
||||||
|
refIdLast: 0,
|
||||||
|
firstChunk: true,
|
||||||
|
isOpen: false,
|
||||||
|
posts: [],
|
||||||
|
refreshRate: 60,
|
||||||
|
swipe: {x: null, y: null}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
emits: ['request-last-update', 'new-markers', 'toggle'],
|
||||||
|
inject: ['api', 'lang', 'consts', 'hash', 'isMobile'],
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
feed: {
|
||||||
|
checkNewFeed: this.checkNewFeed,
|
||||||
|
toggle: this.toggle
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
project() {
|
||||||
|
this.syncUpdateTimer();
|
||||||
|
},
|
||||||
|
modeHisto() {
|
||||||
|
this.syncUpdateTimer();
|
||||||
|
},
|
||||||
|
firstChunk() {
|
||||||
|
this.syncUpdateTimer();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getScrollElement()?.addEventListener('scroll', this.onFeedScroll);
|
||||||
|
this.syncUpdateTimer();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.getScrollElement()?.removeEventListener('scroll', this.onFeedScroll);
|
||||||
|
this.setUpdateTimer(-1);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async init(pMapIsReady) {
|
||||||
|
this.setUpdateTimer(-1);
|
||||||
|
this.loading = false;
|
||||||
|
this.updatable = true;
|
||||||
|
this.outOfData = false;
|
||||||
|
this.refIdFirst = 0;
|
||||||
|
this.refIdLast = 0;
|
||||||
|
this.firstChunk = true;
|
||||||
|
this.posts = [];
|
||||||
|
this.swipe = {x: null, y: null};
|
||||||
|
|
||||||
|
this.toggle(!this.isMobile(), 0);
|
||||||
|
|
||||||
|
await this.getNextFeed();
|
||||||
|
this.getScrollElement().scrollTop = 0;
|
||||||
|
this.syncUpdateTimer();
|
||||||
|
|
||||||
|
//Direct link post action
|
||||||
|
await (this.hash.items.length == 3)
|
||||||
|
? this.findPost(this.hash.items[1], this.hash.items[2], pMapIsReady)
|
||||||
|
: Promise.resolve();
|
||||||
|
},
|
||||||
|
getScrollElement() {
|
||||||
|
return this.$refs.feedSimpleBar?.scrollElement;
|
||||||
|
},
|
||||||
|
async findPost(sPostType, iPostId, pMapIsReady = Promise.resolve()) {
|
||||||
|
let vPost = await this.goToPost(sPostType, iPostId);
|
||||||
|
if(vPost) {
|
||||||
|
await pMapIsReady;
|
||||||
|
await vPost.executeMainAction(0);
|
||||||
|
return vPost;
|
||||||
|
}
|
||||||
|
else if(!this.outOfData) {
|
||||||
|
await this.getNextFeed();
|
||||||
|
await this.$nextTick();
|
||||||
|
return this.findPost(sPostType, iPostId, pMapIsReady);
|
||||||
|
}
|
||||||
|
else console.log('Missing element ID "'+iPostId+'" of type "'+sPostType+'"');
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
async goToPost(sPostType, iPostId) {
|
||||||
|
let avPosts = this.$refs.posts.filter((post) => {return post.postId == sPostType+'-'+iPostId;});
|
||||||
|
if(avPosts.length == 0) return null;
|
||||||
|
|
||||||
|
//Force next update to have enough subsequent elements to position the post on top of the page
|
||||||
|
await this.getNextFeed();
|
||||||
|
|
||||||
|
let vPost = avPosts[0];
|
||||||
|
this.getScrollElement().scrollTop += Math.round(
|
||||||
|
vPost.$el.getBoundingClientRect().top
|
||||||
|
+ window.pageYOffset
|
||||||
|
- parseFloat(getComputedStyle(this.$refs.feedSimpleBar.$el).paddingTop)
|
||||||
|
);
|
||||||
|
|
||||||
|
return vPost;
|
||||||
|
},
|
||||||
|
async getNextFeed() {
|
||||||
|
if(!this.project || this.outOfData || this.loading) return true;
|
||||||
|
|
||||||
|
//Get next chunk
|
||||||
|
this.loading = true;
|
||||||
|
let aoData = await this.api.get('next_feed', {id_project: this.project.id, id: this.refIdLast});
|
||||||
|
let iPostCount = Object.keys(aoData.feed).length;
|
||||||
|
|
||||||
|
//Update pointers
|
||||||
|
this.outOfData = (iPostCount < this.consts.chunk_size);
|
||||||
|
if(iPostCount > 0) {
|
||||||
|
this.refIdLast = aoData.ref_id_last;
|
||||||
|
if(this.firstChunk) this.refIdFirst = aoData.ref_id_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add posts
|
||||||
|
this.posts.push(...aoData.feed);
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
this.firstChunk = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onFeedScroll(oEvent) {
|
||||||
|
const box = oEvent.currentTarget;
|
||||||
|
const content = box.querySelector('.simplebar-content');
|
||||||
|
|
||||||
|
if((box.scrollTop + box.clientHeight) / (content?.offsetHeight || 1) >= 0.8) this.getNextFeed();
|
||||||
|
},
|
||||||
|
onTouchStart(oEvent) {
|
||||||
|
if(!this.isMobile() || !this.isOpen || oEvent.touches.length != 1) return;
|
||||||
|
|
||||||
|
const oTouch = oEvent.touches[0];
|
||||||
|
this.swipe = {x: oTouch.clientX, y: oTouch.clientY};
|
||||||
|
},
|
||||||
|
onTouchEnd(oEvent) {
|
||||||
|
const oTouch = oEvent.changedTouches[0];
|
||||||
|
if(!oTouch || this.swipe.x === null) return;
|
||||||
|
|
||||||
|
const iDeltaX = oTouch.clientX - this.swipe.x;
|
||||||
|
const iDeltaY = oTouch.clientY - this.swipe.y;
|
||||||
|
|
||||||
|
if(iDeltaX > 80 && Math.abs(iDeltaX) > Math.abs(iDeltaY) * 1.5) this.toggle();
|
||||||
|
|
||||||
|
this.swipe = {x: null, y: null};
|
||||||
|
},
|
||||||
|
setUpdateTimer(iSeconds) {
|
||||||
|
if(typeof this.feedTimer != 'undefined') clearTimeout(this.feedTimer);
|
||||||
|
if(iSeconds >= 0) this.feedTimer = setTimeout(this.onUpdateTimer, iSeconds * 1000);
|
||||||
|
},
|
||||||
|
syncUpdateTimer() {
|
||||||
|
this.setUpdateTimer((!!this.project && !this.modeHisto && !this.firstChunk)?this.refreshRate:-1);
|
||||||
|
},
|
||||||
|
async onUpdateTimer() {
|
||||||
|
await this.checkNewFeed();
|
||||||
|
this.syncUpdateTimer();
|
||||||
|
},
|
||||||
|
async checkNewFeed() {
|
||||||
|
if(!this.project) return;
|
||||||
|
|
||||||
|
let aoData = await this.api.get('new_feed', {id_project: this.project.id, id: this.refIdFirst});
|
||||||
|
const aoFeed = aoData.feed || [];
|
||||||
|
const aoMarkers = aoData.markers || [];
|
||||||
|
|
||||||
|
if(aoFeed.length > 0) {
|
||||||
|
//Update pointer
|
||||||
|
this.refIdFirst = aoData.ref_id_first;
|
||||||
|
|
||||||
|
//Add new posts
|
||||||
|
this.posts.unshift(...aoFeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(aoMarkers.length > 0) this.$emit('new-markers', aoMarkers);
|
||||||
|
this.$emit('request-last-update');
|
||||||
|
},
|
||||||
|
toggle(bShow, iAnimDuration=500) {
|
||||||
|
this.isOpen = (typeof bShow == 'boolean')?bShow:(!this.isOpen);
|
||||||
|
this.$emit('toggle', this.isOpen, iAnimDuration);
|
||||||
|
return this.isOpen;
|
||||||
|
},
|
||||||
|
getWidth() {
|
||||||
|
return this.$el.getBoundingClientRect().width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="feed" class="panel panel-right" @touchstart.passive="onTouchStart" @touchend.passive="onTouchEnd">
|
||||||
|
<Simplebar id="feed-panel" class="panel-content" ref="feedSimpleBar">
|
||||||
|
<div id="feed-header">
|
||||||
|
<ProjectPost v-if="modeHisto" :options="{type: 'archived', headerless: true}" />
|
||||||
|
<ProjectPost v-else :options="{type: 'poster', relative_time: lang.get('post.new_message')}" />
|
||||||
|
</div>
|
||||||
|
<div v-if="project" id="feed-posts">
|
||||||
|
<ProjectPost v-for="post in posts" :options="post" ref="posts" />
|
||||||
|
</div>
|
||||||
|
<div id="feed-footer" v-if="loading">
|
||||||
|
<ProjectPost :options="{type: 'loading', headerless: true}" />
|
||||||
|
</div>
|
||||||
|
</Simplebar>
|
||||||
|
<div v-if="project" :class="'panel-control panel-control-'+(isMobile()?'bottom':'top')" @click="toggle">
|
||||||
|
<div class="panel-control-elem panel-control-icon">
|
||||||
|
<SpotIcon :icon="isOpen?'next':'post'" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -13,5 +13,6 @@ export default {
|
|||||||
:title="lang.get('map.see_on_google')"
|
:title="lang.get('map.see_on_google')"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
|
@click.stop
|
||||||
>{{ options.lat_dms+' '+options.lon_dms }}</a>
|
>{{ options.lat_dms+' '+options.lon_dms }}</a>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import spotIcon from '@components/spotIcon';
|
import spotIcon from '@components/spotIcon';
|
||||||
import projectRelTime from '@components/projectRelTime';
|
import projectRelTime from '@components/projectRelTime';
|
||||||
|
import projectMapLink from '@components/projectMapLink';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
spotIcon,
|
spotIcon,
|
||||||
projectRelTime
|
projectRelTime,
|
||||||
|
projectMapLink
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
options: Object,
|
options: Object,
|
||||||
@@ -17,7 +19,7 @@ export default {
|
|||||||
title:''
|
title:''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inject: ['lang'],
|
inject: ['lang', 'isMobile'],
|
||||||
mounted() {
|
mounted() {
|
||||||
this.title =
|
this.title =
|
||||||
(this.$refs.comment?this.$refs.comment.outerHTML:'') +
|
(this.$refs.comment?this.$refs.comment.outerHTML:'') +
|
||||||
@@ -54,18 +56,36 @@ export default {
|
|||||||
/>
|
/>
|
||||||
<span class="drill-icon"><spotIcon :icon="'drill-'+options.subtype" /></span>
|
<span class="drill-icon"><spotIcon :icon="'drill-'+options.subtype" /></span>
|
||||||
<div v-if="options.comment && type == 'post'" class="comment">
|
<div v-if="options.comment && type == 'post'" class="comment">
|
||||||
<p>{{ options.comment }}</p>
|
<p>
|
||||||
|
<spotIcon icon="post" :text="options.comment" size="lg" />
|
||||||
|
</p>
|
||||||
|
<p v-if="!isMobile() && options.latitude && options.longitude">
|
||||||
|
<spotIcon icon="coords" margin="right" size="lg" />
|
||||||
|
<projectMapLink :options="options" />
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<div style="display:none">
|
<div style="display:none">
|
||||||
<span v-if="options.comment" ref="comment" class="lb-caption-line comment desktop">
|
<span v-if="options.comment" ref="comment" class="lb-caption-line comment desktop">
|
||||||
<spotIcon :icon="'post'" width="fixed" size="lg" :text-classes="'comment-text'" :text="options.comment" />
|
<spotIcon icon="post" width="fixed" size="lg" text-classes="comment-text" :text="options.comment" />
|
||||||
</span>
|
</span>
|
||||||
<span ref="postedon" class="lb-caption-line">
|
<span ref="postedon" class="lb-caption-line">
|
||||||
<projectRelTime :icon="'upload'" :localTime="options.posted_on_formatted_time_local" :siteTime="options.posted_on_formatted_time" :offset="options.posted_on_day_offset" />
|
<projectRelTime
|
||||||
|
icon="upload"
|
||||||
|
titleWrapperName="media.posted_on"
|
||||||
|
:localTime="options.posted_on_formatted_time_local"
|
||||||
|
:siteTime="options.posted_on_formatted_time"
|
||||||
|
:offset="options.posted_on_day_offset"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span ref="takenon" class="lb-caption-line">
|
<span ref="takenon" class="lb-caption-line">
|
||||||
<projectRelTime :icon="options.subtype+'-shot'" :localTime="options.taken_on_formatted_time_local" :siteTime="options.taken_on_formatted_time" :offset="options.taken_on_day_offset" />
|
<projectRelTime
|
||||||
|
:icon="options.subtype+'-shot'"
|
||||||
|
:titleWrapperName="'media.'+options.subtype+'_taken_on'"
|
||||||
|
:localTime="options.taken_on_formatted_time_local"
|
||||||
|
:siteTime="options.taken_on_formatted_time"
|
||||||
|
:offset="options.taken_on_day_offset"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default {
|
|||||||
const sAction = this.action;
|
const sAction = this.action;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
this.api.request(sAction, {'email': this.user.email, 'name': this.user.name})
|
this.api.request(sAction, {'email': this.user.email, 'name': this.user.name}, 'POST')
|
||||||
.then((asResponse) => {
|
.then((asResponse) => {
|
||||||
this.feedbacks.push({type: asResponse.result, msg: asResponse.desc});
|
this.feedbacks.push({type: asResponse.result, msg: asResponse.desc});
|
||||||
this.user.setInfo(asResponse.data);
|
this.user.setInfo(asResponse.data);
|
||||||
@@ -53,7 +53,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1><SpotIcon :icon="'newsletter'" width="fixed" :text="lang.get('newsletter.title')" /></h1>
|
|
||||||
<div class="newsletter-form">
|
<div class="newsletter-form">
|
||||||
<input type="email" name="email" id="email" :placeholder="lang.get('newsletter.email_placeholder')" v-model="user.email" :disabled="loading || subscribed" />
|
<input type="email" name="email" id="email" :placeholder="lang.get('newsletter.email_placeholder')" v-model="user.email" :disabled="loading || subscribed" />
|
||||||
<SpotButton :classes="buttonClasses" :title="lang.get('newsletter.'+action)" :icon="action" @click="manage" />
|
<SpotButton :classes="buttonClasses" :title="lang.get('newsletter.'+action)" :icon="action" @click="manage" />
|
||||||
|
|||||||
@@ -13,10 +13,18 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
options: Object,
|
options: Object,
|
||||||
project: Object
|
project: Object,
|
||||||
|
hikes: Object
|
||||||
},
|
},
|
||||||
inject: ['lang', 'consts'],
|
inject: ['lang', 'consts', 'isMobile'],
|
||||||
computed: {
|
computed: {
|
||||||
|
activeTimeInterval() {
|
||||||
|
if(this.options.mode == this.consts.modes.blog) return this.lang.get('project.wip');
|
||||||
|
|
||||||
|
const sYearFrom = this.options.active_from.substr(0, 4);
|
||||||
|
const sYearTo = this.options.active_to.substr(0, 4);
|
||||||
|
return (sYearFrom == sYearTo)?sYearFrom:(sYearFrom + '-' + sYearTo);
|
||||||
|
},
|
||||||
timeinfo() {
|
timeinfo() {
|
||||||
return (this.options.type == 'media')?
|
return (this.options.type == 'media')?
|
||||||
{
|
{
|
||||||
@@ -45,32 +53,45 @@ export default {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="options.type+' '+options.subtype">
|
<div :class="options.type+' '+options.subtype">
|
||||||
<div class="header" v-if="options.type=='track'">
|
<div class="header" v-if="options.type == 'track' || options.type == 'project'">
|
||||||
<h1>
|
<h1>
|
||||||
<spotIcon :icon="options.subtype" size="lg" :text="this.options.name" width="auto" :textClasses="options.subtype" />
|
<spotIcon v-if="options.type == 'project'" :icon="options.subtype" :classes="'track-'+options.subtype" size="lg" :text="this.options.name" width="auto" />
|
||||||
|
<span v-else class="track" :title="lang.get('track.'+options.subtype)">
|
||||||
|
<span class="line" :style="'background-color:'+hikes.colors[options.subtype]+';'"></span>
|
||||||
|
<span class="desc">{{ this.options.name }}</span>
|
||||||
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p v-if="options.description" class="description">{{ options.description }}</p>
|
<p v-if="options.description && !options.leg" class="description">{{ options.description }}</p>
|
||||||
<div v-if="options.subtype!='hitchhiking'" class="separator"></div>
|
<div v-if="options.subtype!='hitchhiking'" class="separator"></div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="options.type=='track'">
|
<div v-if="options.type=='track'">
|
||||||
|
<div v-if="options.leg" class="section destination">
|
||||||
|
<spotIcon :title="lang.get('stats.from')" icon="start" width="fixed" size="lg" :text="options.leg.from" />
|
||||||
|
<spotIcon :title="lang.get('stats.to')" icon="start" transform="rotate-180" width="fixed" size="lg" :text="options.leg.to" />
|
||||||
|
</div>
|
||||||
|
<div v-if="options.leg" class="separator"></div>
|
||||||
<div v-if="options.subtype!='hitchhiking'" class="section track-stats">
|
<div v-if="options.subtype!='hitchhiking'" class="section track-stats">
|
||||||
<spotIcon :title="lang.get('stats.distance')" :icon="'distance'" width="fixed" size="lg" :text="options.distance+'km'" />
|
<spotIcon :title="lang.get('stats.distance')" icon="distance" width="fixed" size="lg" :text="options.distance+'km'" />
|
||||||
<spotIcon :title="lang.get('stats.duration')" :icon="'time'" width="fixed" size="lg" :text="options.duration" />
|
<spotIcon :title="lang.get('stats.duration')" icon="time" width="fixed" size="lg" :text="options.duration" />
|
||||||
<spotIcon :title="lang.get('stats.elevation_gain')" :icon="'elev-gain'" width="fixed" size="lg" :text="options.elev_gain+'m'" />
|
<spotIcon :title="lang.get('stats.elevation_gain')" icon="elev-gain" width="fixed" size="lg" :text="options.elev_gain+'m'" />
|
||||||
<spotIcon :title="lang.get('stats.elevation_loss')" :icon="'elev-drop'" width="fixed" size="lg" :text="options.elev_drop+'m'" />
|
<spotIcon :title="lang.get('stats.elevation_loss')" icon="elev-drop" width="fixed" size="lg" :text="options.elev_drop+'m'" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="options.type=='project'" class="section year">{{ activeTimeInterval }}</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="section coordinates">
|
<div class="section" v-if="options.comment">
|
||||||
<spotIcon :icon="'coords'" width="fixed" size="lg" margin="right" />
|
<spotIcon icon="post" width="fixed" size="lg" :text="options.comment" />
|
||||||
<projectMapLink :options="options" />
|
|
||||||
</div>
|
|
||||||
<div v-if="options.altitude" class="section altitude">
|
|
||||||
<spotIcon :icon="'altitude'" width="fixed" size="lg" :text="options.altitude+'m'" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="section time">
|
<div class="section time">
|
||||||
<projectRelTime :icon="timeinfo.icon" :localTime="localTime" :siteTime="timeinfo.site_time" :offset="timeinfo.offset" />
|
<projectRelTime :icon="timeinfo.icon" :localTime="localTime" :siteTime="timeinfo.site_time" :offset="timeinfo.offset" />
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="!isMobile()" class="section coordinates">
|
||||||
|
<spotIcon icon="coords" width="fixed" size="lg" margin="right" />
|
||||||
|
<projectMapLink :options="options" />
|
||||||
|
</div>
|
||||||
|
<div v-if="options.altitude" class="section altitude">
|
||||||
|
<spotIcon icon="altitude" width="fixed" size="lg" :text="options.altitude+'m'" />
|
||||||
|
</div>
|
||||||
<div class="section weather" v-if="options.weather_icon && options.weather_icon!='unknown'" :title="options.weather_cond==''?'':lang.get('weather.'+options.weather_icon)">
|
<div class="section weather" v-if="options.weather_icon && options.weather_icon!='unknown'" :title="options.weather_cond==''?'':lang.get('weather.'+options.weather_icon)">
|
||||||
<spotIcon :icon="options.weather_icon" width="fixed" size="lg" :text="options.weather_temp+'°C'" />
|
<spotIcon :icon="options.weather_icon" width="fixed" size="lg" :text="options.weather_temp+'°C'" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
focusZoomLevel: 15
|
focusZoomLevel: 15
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
inject: ['api', 'lang', 'project', 'feed', 'user', 'map', 'hash', 'consts', 'isMobile', 'getAnchor'],
|
||||||
computed: {
|
computed: {
|
||||||
postClass() {
|
postClass() {
|
||||||
let sHeaderLess = this.options.headerless?' headerless':'';
|
let sHeaderLess = this.options.headerless?' headerless':'';
|
||||||
@@ -55,11 +56,14 @@
|
|||||||
drillMainIcon() {
|
drillMainIcon() {
|
||||||
return this.mouseOverDrill?'drill-message':'marker';
|
return this.mouseOverDrill?'drill-message':'marker';
|
||||||
},
|
},
|
||||||
|
drillSubIcon() {
|
||||||
|
return this.mouseOverDrill?null:'footprint';
|
||||||
|
},
|
||||||
anchorLink() {
|
anchorLink() {
|
||||||
return '#'+[this.hash.page, this.project.currProject.codename, this.options.type, this.options.id].join(this.consts.hash_sep);
|
return this.getAnchor([this.hash.page, this.project.project.codename, this.options.type, this.options.id]);
|
||||||
},
|
},
|
||||||
modeHisto() {
|
modeHisto() {
|
||||||
return (this.project.currProject.mode == this.consts.modes.histo);
|
return (this.project?.project?.mode == this.consts.modes.histo);
|
||||||
},
|
},
|
||||||
relTime() {
|
relTime() {
|
||||||
return this.modeHisto?(this.options.formatted_time || '').substr(0, 10):this.options.relative_time;
|
return this.modeHisto?(this.options.formatted_time || '').substr(0, 10):this.options.relative_time;
|
||||||
@@ -87,7 +91,6 @@
|
|||||||
return new LngLat(oRelatedMarker.longitude, oRelatedMarker.latitude);
|
return new LngLat(oRelatedMarker.longitude, oRelatedMarker.latitude);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inject: ['api', 'lang', 'project', 'user', 'map', 'hash', 'consts', 'isMobile'],
|
|
||||||
methods: {
|
methods: {
|
||||||
copyAnchor() {
|
copyAnchor() {
|
||||||
copyTextToClipboard(this.consts.server+this.anchorLink);
|
copyTextToClipboard(this.consts.server+this.anchorLink);
|
||||||
@@ -103,8 +106,8 @@
|
|||||||
|
|
||||||
this.popupRequested = true;
|
this.popupRequested = true;
|
||||||
|
|
||||||
if(this.isMobile()) this.project.toggleFeedPanel(false, 'panToInstant');
|
if(this.isMobile()) this.feed.toggle(false);
|
||||||
this.hash.items = [this.project.currProject.codename, this.options.type, this.options.id];
|
this.hash.items = [this.project.project.codename, this.options.type, this.options.id];
|
||||||
|
|
||||||
return this.map.panToBetweenPanels(this.relatedMarkerLatLng, this.focusZoomLevel, iAnimDuration).then(() => {
|
return this.map.panToBetweenPanels(this.relatedMarkerLatLng, this.focusZoomLevel, iAnimDuration).then(() => {
|
||||||
this.openMarkerPopup();
|
this.openMarkerPopup();
|
||||||
@@ -131,17 +134,17 @@
|
|||||||
send() {
|
send() {
|
||||||
if(this.postMessage != '' && this.user.name != '') {
|
if(this.postMessage != '' && this.user.name != '') {
|
||||||
this.sending = true;
|
this.sending = true;
|
||||||
this.api.get(
|
this.api.post(
|
||||||
'add_post',
|
'add_post',
|
||||||
{
|
{
|
||||||
id_project: this.project.currProject.id,
|
id_project: this.project.project.id,
|
||||||
name: this.user.name,
|
name: this.user.name,
|
||||||
content: this.postMessage
|
content: this.postMessage
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.postMessage = '';
|
this.postMessage = '';
|
||||||
this.project.checkNewFeed();
|
this.feed.checkNewFeed();
|
||||||
this.sending = false;
|
this.sending = false;
|
||||||
})
|
})
|
||||||
.catch((sDesc) => {
|
.catch((sDesc) => {
|
||||||
@@ -186,18 +189,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div v-if="options.type == 'message'" class="body-box">
|
<div v-if="options.type == 'message'" class="body-box">
|
||||||
<div class="drill" @click.prevent="panMapToMarker" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
|
<div class="drill" @click.prevent="panMapToMarker" @pointerenter="onMouseEnter" @pointerleave="onMouseLeave">
|
||||||
<span v-if="options.weather_icon && options.weather_icon!='unknown'" class="weather clickable" :title="lang.get('weather.'+options.weather_icon)">
|
<span v-if="options.weather_icon && options.weather_icon!='unknown'" class="weather clickable" :title="lang.get('weather.'+options.weather_icon)">
|
||||||
<spotIcon :icon="options.weather_icon" :text="Math.round(options.weather_temp)+'°C'" text-classes="temperature" />
|
<spotIcon :icon="options.weather_icon" :text="Math.round(options.weather_temp)+'°C'" text-classes="temperature" />
|
||||||
</span>
|
</span>
|
||||||
<img class="staticmap clickable" :title="lang.get('media.click_zoom')" :src="options.static_img_url" />
|
<img class="staticmap clickable" :title="lang.get('media.click_zoom')" :src="options.static_img_url" />
|
||||||
<spotIconStack :mainClasses="'message drill-icon'" :iconMain="drillMainIcon" iconSub="footprint" :icon-sub-transform="'rotate-270'" />
|
<spotIconStack mainClasses="message drill-icon" :iconMain="drillMainIcon" :iconSub="drillSubIcon" icon-sub-transform="rotate-270" />
|
||||||
<div class="comment">
|
<div class="comment" @click.stop>
|
||||||
<p>
|
<p v-if="!isMobile()">
|
||||||
<spotIcon :icon="'coords'" margin="right" size="lg" />
|
<spotIcon :icon="'coords'" margin="right" size="lg" />
|
||||||
<projectMapLink :options="options" />
|
<projectMapLink :options="options" />
|
||||||
</p>
|
</p>
|
||||||
<p v-if="timeDiff">
|
<p>
|
||||||
<projectRelTime :icon="'time'" :localTime="absTimeLocal" :siteTime="options.formatted_time" :offset="options.day_offset" />
|
<projectRelTime :icon="'time'" :localTime="absTimeLocal" :siteTime="options.formatted_time" :offset="options.day_offset" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,11 +11,20 @@ export default {
|
|||||||
offset: String,
|
offset: String,
|
||||||
classes: String,
|
classes: String,
|
||||||
icon: String,
|
icon: String,
|
||||||
|
titleWrapperName: String
|
||||||
},
|
},
|
||||||
inject: ['lang'],
|
inject: ['lang'],
|
||||||
computed: {
|
computed: {
|
||||||
title() {
|
title() {
|
||||||
if(this.localTime != this.siteTime) return this.lang.get('time.user', this.siteTime.slice(-5)) + ((this.offset != '0')?' ('+this.lang.get('unit.day_short')+this.offset+')':'');
|
const bDifferentTimeZone = (this.localTime != this.siteTime);
|
||||||
|
const sTime =
|
||||||
|
this.lang.get('time.user', this.siteTime.slice(-5))
|
||||||
|
+ ((this.offset != '0')?' ('+this.lang.get('unit.day_short')+this.offset+')':'');
|
||||||
|
|
||||||
|
return (this.titleWrapperName)?
|
||||||
|
this.lang.get(this.titleWrapperName, bDifferentTimeZone?sTime:this.siteTime)
|
||||||
|
:
|
||||||
|
(bDifferentTimeZone?sTime:null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
154
src/components/projectSettings.vue
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
<script>
|
||||||
|
import Simplebar from 'simplebar-vue';
|
||||||
|
|
||||||
|
import SpotIcon from '@components/spotIcon';
|
||||||
|
import ProjectNewsletter from '@components/projectNewsletter';
|
||||||
|
import logoIconUrl from '@images/icons/favicon.svg';
|
||||||
|
import logoTitleUrl from '@images/logo_title.svg';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SpotIcon,
|
||||||
|
ProjectNewsletter,
|
||||||
|
Simplebar
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
projects: Array,
|
||||||
|
projectCodeName: String,
|
||||||
|
baseMaps: Array,
|
||||||
|
baseMap: String,
|
||||||
|
mapInitializing: Boolean,
|
||||||
|
hikes: Object
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isOpen: false,
|
||||||
|
lastUpdate: {unix_time: 0, relative_time: '', formatted_time: ''},
|
||||||
|
logoIconUrl,
|
||||||
|
logoTitleUrl
|
||||||
|
};
|
||||||
|
},
|
||||||
|
emits: ['update:baseMap', 'update:projectCodeName', 'toggle'],
|
||||||
|
inject: ['api', 'lang', 'user', 'consts', 'isMobile', 'hash'],
|
||||||
|
computed: {
|
||||||
|
project() {
|
||||||
|
return this.projects.find((project) => project.codename == this.projectCodeName);
|
||||||
|
},
|
||||||
|
projectCodeNameModel: {
|
||||||
|
get() {
|
||||||
|
return this.projectCodeName;
|
||||||
|
},
|
||||||
|
set(sProjectCodeName) {
|
||||||
|
this.$emit('update:projectCodeName', sProjectCodeName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
baseMapModel: {
|
||||||
|
get() {
|
||||||
|
return this.baseMap;
|
||||||
|
},
|
||||||
|
set(sBaseMap) {
|
||||||
|
this.$emit('update:baseMap', sBaseMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
projectCodeName() {
|
||||||
|
this.setLastUpdate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.setLastUpdate();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async setLastUpdate() {
|
||||||
|
if(this.project?.mode == this.consts.modes.blog) {
|
||||||
|
this.lastUpdate = await this.api.get('last_update', {id_project: this.project.id});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggle(bShow, iAnimDuration=500) {
|
||||||
|
this.isOpen = (typeof bShow == 'boolean')?bShow:(!this.isOpen);
|
||||||
|
this.$emit('toggle', this.isOpen, iAnimDuration);
|
||||||
|
return this.isOpen;
|
||||||
|
},
|
||||||
|
getWidth() {
|
||||||
|
return this.$el.getBoundingClientRect().width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="settings" class="panel panel-left">
|
||||||
|
<div id="settings-panel" class="panel-content">
|
||||||
|
<div class="settings-header settings-box">
|
||||||
|
<img :src="logoIconUrl" class="logo-icon clickable" :title="lang.get('project.overview')" @click="hash.items = []" />
|
||||||
|
<img :src="logoTitleUrl" class="logo-title" :alt="consts.title" />
|
||||||
|
<div class="last_update" v-if="project?.mode == consts.modes.blog && lastUpdate.unix_time > 0" :title="lastUpdate.formatted_time">
|
||||||
|
<SpotIcon icon="find-me-spot" :text="lang.get('feed.last_update')+' '+lastUpdate.relative_time" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-sections">
|
||||||
|
<Simplebar id="settings-sections-scrollbox">
|
||||||
|
<div class="settings-section settings-box">
|
||||||
|
<h1><SpotIcon :icon="'project'" width="fixed" :text="lang.get('project.hikes')" /></h1>
|
||||||
|
<div class="settings-section-body">
|
||||||
|
<div class="radio" v-for="project in projects" :key="'project-'+project.id">
|
||||||
|
<input type="radio" :id="'project-'+project.id" :value="project.codename" v-model="projectCodeNameModel" :disabled="mapInitializing" />
|
||||||
|
<label :for="'project-'+project.id">
|
||||||
|
<span>{{ project.name }}</span>
|
||||||
|
<a v-if="!isMobile() && project.gpxfilepath" class="download" :href="project.gpxfilepath" :download="project.codename + '.gpx'" :title="lang.get('track.download')" @click.stop="()=>{}">
|
||||||
|
<SpotIcon :icon="'download'" margin="left" />
|
||||||
|
</a>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-section settings-box">
|
||||||
|
<h1><SpotIcon :icon="'map'" width="fixed" :text="lang.get('map.title')" /></h1>
|
||||||
|
<div class="settings-section-body">
|
||||||
|
<div class="radio" v-for="bm in baseMaps" :key="'map-'+bm.id_map">
|
||||||
|
<input type="radio" :id="'map-'+bm.id_map" :value="bm.codename" v-model="baseMapModel" :disabled="mapInitializing" />
|
||||||
|
<label :for="'map-'+bm.id_map">{{ lang.get('map.'+bm.codename) }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-section settings-box newsletter">
|
||||||
|
<h1><SpotIcon :icon="'newsletter'" width="fixed" :text="lang.get('newsletter.title')" /></h1>
|
||||||
|
<div class="settings-section-body">
|
||||||
|
<ProjectNewsletter />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-section settings-box admin" v-if="user.hasClearance(consts.clearances.admin)">
|
||||||
|
<h1><SpotIcon :icon="'admin'" width="fixed" :text="lang.get('admin.title')" /></h1>
|
||||||
|
<div class="settings-section-body admin-actions">
|
||||||
|
<a class="button" href="#admin"><SpotIcon :icon="'config'" :text="lang.get('admin.config')" /></a>
|
||||||
|
<a class="button" href="#upload"><SpotIcon :icon="'upload'" :text="lang.get('admin.upload')" /></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Simplebar>
|
||||||
|
</div>
|
||||||
|
<div v-if="!isMobile()" class="settings-footer">
|
||||||
|
<a href="https://git.lutran.fr/franzz/spot" :title="lang.get('credits.git')" target="_blank" rel="noopener">
|
||||||
|
<SpotIcon :icon="'credits'" width="auto" :text="lang.get('credits.project', consts.title)" />
|
||||||
|
</a>
|
||||||
|
<span> {{ lang.get('credits.license') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="'panel-control panel-control-'+(isMobile()?'bottom':'top')">
|
||||||
|
<div class="panel-control-elem panel-control-icon" @click="toggle">
|
||||||
|
<SpotIcon :icon="isOpen?'prev':'menu'" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="project?.id && !isMobile()" id="legend" class="panel-control panel-control-bottom">
|
||||||
|
<div class="panel-control-elem">
|
||||||
|
<div v-for="(color, hikeType) in hikes.colors" class="track">
|
||||||
|
<span class="line" :style="'background-color:'+color+';'"></span>
|
||||||
|
<span class="desc">{{ lang.get('track.'+hikeType) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="project?.id" id="title" :class="'panel-control panel-control-'+(isMobile()?'bottom':'top')">
|
||||||
|
<div class="panel-control-elem"><span>{{ project.name }}</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -70,7 +70,7 @@ export default {
|
|||||||
.spot-icon-with-text {
|
.spot-icon-with-text {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: var.$elem-spacing;
|
gap: var.$text-spacing;
|
||||||
|
|
||||||
.spot-icon-symbol {
|
.spot-icon-symbol {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
@@ -80,16 +80,17 @@ export default {
|
|||||||
.spot-icon-text {
|
.spot-icon-text {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.spot-icon {
|
.spot-icon {
|
||||||
&.margin-right {
|
&.margin-right {
|
||||||
margin-right: var.$elem-spacing;
|
margin-right: var.$text-spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.margin-left {
|
&.margin-left {
|
||||||
margin-left: var.$elem-spacing;
|
margin-left: var.$text-spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -45,7 +45,7 @@ export default {
|
|||||||
<span :class="mainClass">
|
<span :class="mainClass">
|
||||||
<FontAwesomeLayers>
|
<FontAwesomeLayers>
|
||||||
<spotIcon :icon="iconMain" :classes="iconMainClass" :transform="iconMainTransform" />
|
<spotIcon :icon="iconMain" :classes="iconMainClass" :transform="iconMainTransform" />
|
||||||
<spotIcon :icon="iconSub" :classes="iconSubClass" :transform="iconSubTransformValue" />
|
<spotIcon v-if="iconSub" :icon="iconSub" :classes="iconSubClass" :transform="iconSubTransformValue" />
|
||||||
</FontAwesomeLayers>
|
</FontAwesomeLayers>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import SpotButton from '@components/spotButton';
|
|||||||
export default {
|
export default {
|
||||||
name: 'upload',
|
name: 'upload',
|
||||||
components: { SpotButton, SpotIcon },
|
components: { SpotButton, SpotIcon },
|
||||||
inject: ['api', 'lang', 'projects', 'consts', 'user'],
|
inject: ['api', 'lang', 'projects', 'consts', 'user', 'getPrevAnchor'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
project: this.projects.getDefaultProject(),
|
project: this.projects.getDefaultProject(),
|
||||||
@@ -51,6 +51,7 @@ export default {
|
|||||||
endpoint,
|
endpoint,
|
||||||
fieldName: 'files[]',
|
fieldName: 'files[]',
|
||||||
formData: true,
|
formData: true,
|
||||||
|
headers: {'X-CSRF-Token': this.consts.csrf_token},
|
||||||
allowedMetaFields: ['t', 'name', 'type'],
|
allowedMetaFields: ['t', 'name', 'type'],
|
||||||
getResponseData(xhr) {
|
getResponseData(xhr) {
|
||||||
return JSON.parse(xhr.responseText || '{}');
|
return JSON.parse(xhr.responseText || '{}');
|
||||||
@@ -65,13 +66,13 @@ export default {
|
|||||||
const uploadedFiles = response?.body?.files || [];
|
const uploadedFiles = response?.body?.files || [];
|
||||||
uploadedFiles.forEach((uploadedFile) => {
|
uploadedFiles.forEach((uploadedFile) => {
|
||||||
const hasError = Object.prototype.hasOwnProperty.call(uploadedFile, 'error');
|
const hasError = Object.prototype.hasOwnProperty.call(uploadedFile, 'error');
|
||||||
this.logs.push(hasError ? uploadedFile.error : this.lang.get('upload.success', [uploadedFile.name]));
|
this.logs.push(hasError ? uploadedFile.error : this.lang.get('upload.success', [uploadedFile.original_name || uploadedFile.name]));
|
||||||
if(!hasError) this.files.push({...uploadedFile, content: ''});
|
if(!hasError) this.files.push({...uploadedFile, content: ''});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.uppy.on('upload-error', (file, error, response) => {
|
this.uppy.on('upload-error', (file, error, response) => {
|
||||||
const message = response?.body?.error || error?.message || this.lang.get('error');
|
const message = response?.body?.error || error?.message || this.lang.get('upload.error');
|
||||||
this.logs.push(message);
|
this.logs.push(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ export default {
|
|||||||
event.target.value = '';
|
event.target.value = '';
|
||||||
},
|
},
|
||||||
addComment(oFile) {
|
addComment(oFile) {
|
||||||
this.api.get('add_comment', {
|
this.api.post('add_comment', {
|
||||||
id: oFile.id,
|
id: oFile.id,
|
||||||
content: oFile.content
|
content: oFile.content
|
||||||
})
|
})
|
||||||
@@ -98,12 +99,12 @@ export default {
|
|||||||
navigator.geolocation.getCurrentPosition(
|
navigator.geolocation.getCurrentPosition(
|
||||||
(position) => {
|
(position) => {
|
||||||
this.logs.push('Sending position...');
|
this.logs.push('Sending position...');
|
||||||
this.api.get('add_position', {
|
this.api.post('add_position', {
|
||||||
'latitude': position.coords.latitude,
|
'latitude': position.coords.latitude,
|
||||||
'longitude': position.coords.longitude,
|
'longitude': position.coords.longitude,
|
||||||
'timestamp': Math.round(position.timestamp / 1000)
|
'timestamp': Math.round(position.timestamp / 1000)
|
||||||
})
|
})
|
||||||
.then((asData) => {this.logs.push(this.lang.get('success'));})
|
.then((asData) => {this.logs.push(this.lang.get('upload.success', [this.lang.get('upload.position.new')]));})
|
||||||
.catch((sMsgId) => {this.logs.push(this.lang.get(sMsgId));});
|
.catch((sMsgId) => {this.logs.push(this.lang.get(sMsgId));});
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
@@ -119,7 +120,7 @@ export default {
|
|||||||
<template>
|
<template>
|
||||||
<div id="upload">
|
<div id="upload">
|
||||||
<div class="section header">
|
<div class="section header">
|
||||||
<a name="back" class="button" href="#project"><SpotIcon :icon="'back'" :text="lang.get('action.back')" /></a>
|
<a name="back" class="button" :href="getPrevAnchor()"><SpotIcon :icon="'back'" :text="lang.get('action.back')" /></a>
|
||||||
<h1>{{ this.project.name }}</h1>
|
<h1>{{ this.project.name }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" v-if="project.editable">
|
<div class="section" v-if="project.editable">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 10 KiB |
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<browserconfig>
|
|
||||||
<msapplication>
|
|
||||||
<tile>
|
|
||||||
<square150x150logo src="/images/icons/mstile-150x150.png?v=GvmqYyKwbb"/>
|
|
||||||
<TileColor>#00a300</TileColor>
|
|
||||||
</tile>
|
|
||||||
</msapplication>
|
|
||||||
</browserconfig>
|
|
||||||
|
Before Width: | Height: | Size: 821 B |
|
Before Width: | Height: | Size: 1.3 KiB |
BIN
src/images/icons/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
73
src/images/icons/favicon.svg
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="240" height="240" viewBox="0 0 240 240" overflow="hidden" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||||
|
<title>LiveTrail</title>
|
||||||
|
<g transform="translate(-494.84 -161.68)">
|
||||||
|
<g transform="translate(9.9388)">
|
||||||
|
<path d="m512 258c0-50.81 41.414-92 92.5-92s92.5 41.19 92.5 92-41.414 92-92.5 92-92.5-41.19-92.5-92z" fill="#071e26" fill-rule="evenodd"/>
|
||||||
|
<g fill-rule="evenodd">
|
||||||
|
<path d="m558.39 229.38c-4.429-5.408-3.634-13.382 1.774-17.81s13.382-3.634 17.81 1.774 3.634 13.382-1.774 17.81-13.382 3.634-17.81-1.774z" fill="#fff"/>
|
||||||
|
<path d="m563.87 225.09c-4.43-5.411-3.635-13.388 1.775-17.818s13.388-3.636 17.818 1.775c4.43 5.41 3.636 13.387-1.775 17.817-5.41 4.431-13.387 3.636-17.818-1.774z" fill="#071e26"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#fff">
|
||||||
|
<g fill-rule="evenodd">
|
||||||
|
<path d="m620 220.8 2.795-1.1463 1.205-2.6566 1.205 2.6566 2.795 1.1463-2.795 1.1463-1.205 2.6566-1.205-2.6566z" stroke-width="1.0424"/>
|
||||||
|
<path d="m587 240 1.4963-0.603 0.64534-1.397 0.64534 1.397 1.4963 0.603-1.4963 0.603-0.64534 1.397-0.64534-1.397z" stroke-width="1.1949"/>
|
||||||
|
<path d="m571 251.16 1.397-0.55454 0.603-1.2857 0.603 1.2857 1.397 0.55454-1.397 0.55453-0.603 1.2857-0.603-1.2857z" stroke-width="1.1076"/>
|
||||||
|
</g>
|
||||||
|
<path d="m629.49 185 0.794 1.7 1.7 0.793-1.7 0.794-0.794 1.7-0.793-1.7-1.7-0.794 1.7-0.793z"/>
|
||||||
|
<path d="m611 198 0.634 1.36 1.36 0.635-1.36 0.634-0.634 1.36-0.635-1.36-1.36-0.634 1.36-0.635z"/>
|
||||||
|
<path transform="matrix(-1.4991,0,0,-1.5,760.18,285.22)" d="m70.493 18.813 0.3174 0.68 0.68 0.3174-0.68 0.3173-0.3174 0.68-0.3173-0.68-0.68-0.3173 0.68-0.3174z"/>
|
||||||
|
<path d="m547.5 207-0.476-1.02-1.02-0.476 1.02-0.476 0.476-1.02 0.476 1.02 1.02 0.476-1.02 0.476z"/>
|
||||||
|
<path d="m575 207-0.318-0.68-0.68-0.317 0.68-0.318 0.318-0.68 0.317 0.68 0.68 0.318-0.68 0.317z"/>
|
||||||
|
<path transform="matrix(-1.8151,0,0,-2,602.88,248.81)" d="m35.247 9.4067 0.1586 0.34 0.34 0.15866-0.34 0.15867-0.1586 0.34-0.1587-0.34-0.34-0.15867 0.34-0.15866z"/>
|
||||||
|
<path d="m533 265-0.634-1.36-1.36-0.635 1.36-0.634 0.634-1.36 0.635 1.36 1.36 0.634-1.36 0.635z"/>
|
||||||
|
<path transform="matrix(1.2,0,0,1.143,372.51,135.24)" d="m176.23 47.033 0.794 1.7 1.7 0.7934-1.7 0.7933-0.794 1.7-0.793-1.7-1.7-0.7933 1.7-0.7934z"/>
|
||||||
|
<path transform="matrix(1,0,0,1.2,374.26,190.56)" d="m176.23 47.033 0.794 1.7 1.7 0.7934-1.7 0.7933-0.794 1.7-0.793-1.7-1.7-0.7933 1.7-0.7934z"/>
|
||||||
|
<path transform="matrix(1.2843,0,0,1.5,579.75,197.78)" d="m70.493 18.813 0.3174 0.68 0.68 0.3174-0.68 0.3173-0.3174 0.68-0.3173-0.68-0.68-0.3173 0.68-0.3174z"/>
|
||||||
|
<path transform="matrix(-1.2,0,0,-1.1072,887.49,304.61)" d="m176.23 47.033 0.794 1.7 1.7 0.7934-1.7 0.7933-0.794 1.7-0.793-1.7-1.7-0.7933 1.7-0.7934z"/>
|
||||||
|
<path d="m646.49 235 0.794 1.7 1.7 0.793-1.7 0.794-0.794 1.7-0.793-1.7-1.7-0.794 1.7-0.793z"/>
|
||||||
|
<path d="m652.49 205 0.794 1.7 1.7 0.793-1.7 0.794-0.794 1.7-0.793-1.7-1.7-0.794 1.7-0.793z"/>
|
||||||
|
<path d="m590.99 213 0.952 2.04 2.04 0.952-2.04 0.952-0.952 2.04-0.952-2.04-2.04-0.952 2.04-0.952z"/>
|
||||||
|
<path d="m562 196-0.318-0.68-0.68-0.317 0.68-0.318 0.318-0.68 0.317 0.68 0.68 0.318-0.68 0.317z"/>
|
||||||
|
<path d="m629 245-0.3172-0.63469-0.68-0.29616 0.68-0.29618 0.3172-0.63469 0.3174 0.63469 0.68 0.29618-0.68 0.29616z" stroke-width="1.9322"/>
|
||||||
|
<path d="m603.51 184-0.794-1.7-1.7-0.793 1.7-0.794 0.794-1.7 0.793 1.7 1.7 0.794-1.7 0.793z"/>
|
||||||
|
</g>
|
||||||
|
<g fill-rule="evenodd">
|
||||||
|
<path d="m532.55 294.65 14.287-13.336 3.834 1.008 16-16.628 9.5 7.222 14.666-17.3 4 0.336 12.834-14.948 11.166 8.902 6.5 9.238 4.167-1.512 14.667 18.139 10.166-7.726 7 5.711 3 4.367 4.167-1.008 15.5 17.132-75.333 11.757z" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
|
||||||
|
<path d="m608.81 246 1.003 8.209-1.839 2.849 0.836 12.9-6.35-8.544-4.011 9.047 7.353 11.057-0.668 6.702 5.013 7.036-8.021-7.706-4.345-2.178-3.676-5.864-8.69-9.215-7.52 5.529-10.861-6.534 0.334 6.869-4.011 2.01 0.836 4.356-3.008 0.503 1.002 3.686-7.5127 6.7787 56.141 16.509 71.187-16.586-13.368-13.404-3.008 0.671-5.849-6.534-4.679-2.513c0.056 1.34 0.111 2.68 0.167 4.02l1.504 4.692-3.509-1.173v3.853l3.008 7.037-27.238-28.984-3.844 1.507-7.185-9.717z" fill="#071e26"/>
|
||||||
|
<path d="m557.76 295.44 7.1017-6.2644 20.228-15.095 1.8517 2.4251-3.1978 2.3216 1.189 3.166-4.588 5.334 2.039-0.334v3.167l-6.117 6.5z" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
|
||||||
|
<g fill="#254711">
|
||||||
|
<path d="m615 255 8.211 18.535 5.194 10.278-3.519 1.011 11.563 16.176 9.551-2.865-7.708-10.615 2.346-2.19-5.53-5.729-5.8039-11.523-3.0701 1.2994z"/>
|
||||||
|
<path d="m661 279 2.667 5.5-1.167 0.833 7 8.667 7.5-0.167-7.667-7.166-0.166-2.334-0.38281-1.3574-2.2842 0.85742z"/>
|
||||||
|
<path d="m572.72 279.92-0.977-5.277 4.808 2.384z"/>
|
||||||
|
<path d="m590 281 5.085 11.5-3.56 0.5 5.763 9.833 12.933 4.7511-4.6275-8.0841 0.678-2.333-4.068-1 1.017-1.5-4.576-6-2.034-0.167z"/>
|
||||||
|
</g>
|
||||||
|
<path d="m527 299.96c18.547-4.603 31.068-3.672 53.401 1.668s69.294 26.311 80.599 30.371c-12.862 13.31-54.572 64.019-54.619 64.003" fill="#335918"/>
|
||||||
|
<path d="m525.5 298.73c18.734-4.58 37.609-1.135 53.942 1.66s32.304 11.898 44.058 15.108" fill="none" stroke="#254711" stroke-miterlimit="8" stroke-width="7"/>
|
||||||
|
<path d="m546 324.3c11.029-3.094 36.132 4.084 59.023-15.243 7.76-3.544 34.236-10.913 48.394-13 14.158-2.088 35.759-5.911 36.555 0.473 0.797 6.384-15.639 20.615-28.285 36.832-13.976 16.715-43.816 47.263-55.073 59.64" fill="#335918"/>
|
||||||
|
<path d="m626.11 302.21c-1.294 0.057-23.877 5.2528-23.602 9.0858 0.276 3.833 29.829 7.526 24.789 13.7-5.041 6.173-51.588 9.809-55.031 23.34-3.443 13.53 26.684 37.26 32.827 43.462 6.142 6.201 13.494-8.5744 11.704-15.029-6.3872-3.715-28.859-12.535-20.899-25.558s37.1-13.22 46.107-23.509c0.083-12.995-29.003-12.46-31.73-16.744-2.726-4.285 17.13-8.8038 15.835-8.7478z" fill="#fff"/>
|
||||||
|
<path d="m669.28 193.75c36.135 36.135 36.606 94.249 1.054 129.8-22.955 22.955-45.516 47.442-65.428 73.44-20.337-26.324-41.044-51.163-64.374-74.493-36.135-36.135-36.606-94.249-1.053-129.8s93.667-35.081 129.8 1.053z" fill="none" stroke="#11db6d" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="9.3333"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<metadata>
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<dc:title>LiveTrail</dc:title>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Franzz</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<cc:license rdf:resource="http://creativecommons.org/licenses/by/4.0/"/>
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License rdf:about="http://creativecommons.org/licenses/by/4.0/">
|
||||||
|
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
||||||
|
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
||||||
|
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
||||||
|
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
||||||
|
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
82
src/images/icons/ogp.svg
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="676.8" height="676.8" overflow="hidden" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||||
|
<title>LiveTrail</title>
|
||||||
|
<g transform="translate(-352.65 -45.724)">
|
||||||
|
<g>
|
||||||
|
<circle cx="691.05" cy="384.12" r="329.31" fill="#081b19" stroke="#11db6d" stroke-width="18.172"/>
|
||||||
|
<circle cx="966.78" cy="384.12" r="15.375" fill="#11db6d"/>
|
||||||
|
<circle cx="413.75" cy="384.12" r="15.375" fill="#11db6d"/>
|
||||||
|
</g>
|
||||||
|
<path d="m524.56 198.65-21.605 22.42-37.647-36.278 8.1713-8.4797 30.768 29.649 13.433-13.94zm23.95-18.263-9.487 6.9765-30.973-42.119 9.487-6.9765zm35.655-78.545 6.1503 54.953-12.054 5.7224-38.493-39.6 11.462-5.441 25.451 28.195q2.0569 2.2926 3.1909 4.2564l0.19761-0.0938q-0.81699-2.3161-1.2773-5.3262l-5.7592-37.382zm68.697 36.114-30.584 6.9072-11.517-50.997 29.41-6.6421 2.1123 9.353-17.924 4.0479 2.5621 11.344 16.679-3.7668 2.1043 9.3174-16.679 3.7668 2.6344 11.665 19.097-4.313zm55.327-45.251-14.91 0.17965 0.51435 42.69-11.812 0.14231-0.51435-42.69-14.837 0.17877-0.11552-9.5879 41.56-0.50073zm60.135 53.926-13.22-2.8598-5.1021-14.868q-0.56711-1.6893-1.1323-3.0426-0.56521-1.3532-1.2431-2.3578-0.63459-1.0325-1.4686-1.6605-0.79065-0.65595-1.8597-0.8872l-3.1002-0.67061-4.2318 19.563-11.51-2.4898 11.054-51.099 18.245 3.9466q18.601 4.0237 15.595 17.921-0.57811 2.6726-1.891 4.7759-1.3052 2.0677-3.195 3.5613-1.8898 1.4936-4.313 2.3496-2.3876 0.86368-5.1508 1.0493l-0.0308 0.14254q1.0632 0.60301 1.9549 1.6538 0.89945 1.0152 1.666 2.3 0.76651 1.2849 1.3876 2.7248 0.66441 1.412 1.1584 2.7498zm-22.17-49.259-3.0756 14.218 4.9888 1.0792q3.706 0.80165 6.4134-0.85078 2.7508-1.6804 3.4445-4.8874 1.4491-6.6992-6.5686-8.4336zm76.826 74.696-11.534-5.6264 1.7567-12.083-16.711-8.1519-8.4085 8.8385-11.469-5.5945 40.026-38.645 12.55 6.122zm-8.2383-27.019 2.6582-18.256q0.30609-2.0412 1.2755-4.6106l-0.26215-0.12787q-1.1708 1.9844-2.8172 3.6964l-12.88 13.432zm31.191 44.764-9.3799-7.1199 31.61-41.643 9.3799 7.1199zm38.869 37.8-21.268-22.739 38.183-35.713 8.0442 8.6004-31.206 29.188 13.224 14.139z" fill="#11db6d" stroke-width="18.172" aria-label="LIVETRAIL"/>
|
||||||
|
<path d="m455.29 516.6-3.5781-7.1421-20.448 10.244-2.8345-5.6578 20.448-10.244-3.5606-7.1072 4.5926-2.3009 9.9733 19.907zm-0.0542 42.226-3.5336-5.2496 9.4623-6.3693-6.4783-9.6243-9.4623 6.3692-3.5227-5.2334 23.234-15.64 3.5227 5.2334-9.2679 6.2384 6.4783 9.6243 9.2679-6.2384 3.5336 5.2496zm21.18 25.407-11.034-12.664 21.117-18.399 10.611 12.178-3.8729 3.3744-6.4666-7.4218-4.6975 4.093 6.0175 6.9064-3.8581 3.3616-6.0175-6.9064-4.83 4.2084 6.89 7.9077zm27.329 26.009-5.3608-4.8752 1.6267-8.2623q0.19067-0.93541 0.29197-1.7145 0.10131-0.77907 0.0567-1.4268-0.017-0.64903-0.23496-1.1641-0.19041-0.51637-0.6239-0.91059l-1.2571-1.1432-7.2143 7.9328-4.6672-4.2445 18.844-20.721 7.3982 6.7281q7.5427 6.8595 2.4178 12.495-0.98555 1.0837-2.1589 1.7063-1.1602 0.60809-2.4373 0.7931-1.2771 0.18502-2.6302-0.0688-1.3387-0.24066-2.6695-0.8965l-0.0526 0.0578q0.33098 0.565 0.46223 1.2916 0.14439 0.71212 0.15476 1.5136 0.0104 0.80142-0.0883 1.6357-0.0711 0.83294-0.2013 1.5857zm2.9481-28.788-5.2432 5.7654 2.0229 1.8397q1.5028 1.3666 3.2015 1.3275 1.7264-0.0404 2.909-1.3409 2.4705-2.7165-0.7807-5.6732zm20.553 46.075-13.652-9.786 16.318-22.763 13.128 9.4105-2.9927 4.1749-8.0005-5.7351-3.6299 5.0638 7.4449 5.3368-2.9813 4.159-7.4449-5.3368-3.7324 5.2067 8.5244 6.1106zm51.993 27.749-6.2412-2.8832 0.80439-6.4916-9.0427-4.1774-4.4037 4.8288-6.2057-2.8668 21.001-21.15 6.7909 3.1371zm-4.7175-14.378 1.2175-9.8076q0.14089-1.0967 0.6311-2.4838l-0.14185-0.0655q-0.60468 1.0761-1.4671 2.0116l-6.7468 7.3393zm41.565 26.457-6.0927-1.8493-5.9343-20.192q-0.5237-1.7714-0.67058-2.6325l-0.0748-0.0227q-0.28161 1.4658-1.1665 4.3813l-4.47 14.727-5.7002-1.7302 8.1346-26.8 6.5039 1.9741 5.682 19.544q0.39419 1.3239 0.68193 2.5951l0.0748 0.0227q0.13411-0.97985 0.96799-3.7272l4.4984-14.821 5.7002 1.7302zm16.136 3.6088 4.3114-27.674 9.8036 1.5273q14.744 2.297 12.642 15.787-1.0072 6.465-5.6419 9.6963-4.6154 3.2343-11.312 2.1911zm9.7541-21.627-2.733 17.542 3.0878 0.48104q4.0527 0.63138 6.728-1.4424 2.6946-2.0708 3.3471-6.2586 0.61634-3.9562-1.3284-6.5719-1.9224-2.632-6.0523-3.2754zm49.427 27.015-1.2062-27.982 10.186-0.43908q4.6832-0.20187 7.2744 1.4068 2.5912 1.6086 2.7258 4.7308 0.0976 2.2635-1.3708 4.0276-1.4488 1.7632-3.8008 2.5293l3e-3 0.0781q3.0015 0.24205 4.8562 1.9997 1.8743 1.7568 1.9878 4.3911 0.16571 3.8441-2.4881 6.2262-2.6546 2.3626-7.4158 2.5679zm5.2967-23.609 0.28599 6.6345 2.7709-0.11944q1.9513-0.0841 3.0232-1.0687 1.0905-1.0049 1.019-2.6636-0.13291-3.0831-4.738-2.8846zm0.48786 11.318 0.31796 7.376 3.4148-0.1472q2.1855-0.0942 3.371-1.1619 1.2051-1.0685 1.1294-2.8247-0.0723-1.6781-1.3429-2.5813-1.251-0.90399-3.4365-0.80978zm53.926 5.6957-6.7786 1.1474-3.0041-5.8106-9.8212 1.6624-0.90517 6.4723-6.7401 1.1409 5.378-29.317 7.3756-1.2484zm-12.016-9.1978-4.5368-8.78q-0.50339-0.98448-0.88267-2.406l-0.15406 0.0261q0.10907 1.2295-0.074 2.4887l-1.42 9.8676zm43.323-0.69612q-2.5124 2.3008-7.2466 3.7168-6.175 1.847-10.797-0.72539-4.6224-2.5724-6.4302-8.6164-1.9253-6.437 0.84394-11.628 2.788-5.1965 9.1314-7.0939 3.9296-1.1753 6.9207-0.98955l1.7406 5.8195q-3.1759-0.8033-6.6189 0.22652-3.7798 1.1306-5.3894 4.201-1.6095 3.0704-0.39499 7.131 1.1642 3.8921 4.0475 5.5576 2.8777 1.6468 6.5827 0.53858 3.5366-1.0578 5.9595-3.658zm35.414-13.44-7.307 3.1795-12.341-7.9q-0.35518-0.22885-1.2962-1.1187l-0.0895 0.039 5.4706 12.572-5.7847 2.5171-11.175-25.682 5.7847-2.5171 5.2836 12.142 0.0895-0.039q-3e-3 -0.59497 0.0929-1.7444l2.4319-13.796 6.895-3.0003-3.6243 16.146zm48.679-30.031-5.6635 3.8974-5.1732-4.0032-8.2057 5.6469 1.907 6.2509-5.6314 3.8753-7.4789-28.852 6.1623-4.2407zm-14.773-3.2766-7.8146-6.05q-0.87143-0.68061-1.8145-1.8098l-0.12871 0.0886q0.6171 1.069 0.98179 2.288l2.8712 9.5468zm40.775-21.699q-1.4746 4.4982-6.0104 8.5677-5.03 4.513-10.429 4.345-5.3973-0.19559-9.7799-5.0802-4.4086-4.9137-4.0177-10.854 0.39096-5.94 5.6536-10.662 3.3146-2.9739 6.6658-4.3276l3.9521 4.4049q-3.6654 0.76959-7.1544 3.9-2.922 2.6217-3.0596 6.1826-0.1361 3.5334 2.7074 6.7025 2.8826 3.2128 6.1036 3.4979 3.2355 0.27211 6.0413-2.2453 1.6864-1.513 2.2445-2.8797l-3.6521-4.0705-4.1723 3.7434-3.3652-3.7507 8.8679-7.9564zm28.042-28.335-4.4833 5.2121-6.0226-2.5526-6.4957 7.5516 3.4371 5.5585-4.4578 5.1825-14.585-25.994 4.8781-5.6711zm-15.121 0.59649-9.0984-3.8588q-1.0161-0.43606-2.2158-1.2876l-0.10189 0.11845q0.86916 0.87645 1.5325 1.9622l5.2093 8.4999zm26.288-16.388-3.6101 5.1735-22.969-16.028 3.6101-5.1735zm20.981-35.404-3.0629 5.5821-20.98 1.6544q-1.8413 0.14816-2.7141 0.11481l-0.0376 0.0685q1.3765 0.57707 4.0477 2.0427l13.493 7.4036-2.8656 5.2225-24.554-13.473 3.2696-5.9588 20.294-1.5408q1.3767-0.11347 2.6798-0.13359l0.0376-0.0685q-0.93131-0.33278-3.4484-1.7139l-13.579-7.4505 2.8656-5.2225z" fill="#11db6d" stroke-width="18.172" aria-label="THERE AND BACK AGAIN"/>
|
||||||
|
<g transform="matrix(1.947 0 0 1.947 -486.68 -164.3)">
|
||||||
|
<path d="m512 258c0-50.81 41.414-92 92.5-92s92.5 41.19 92.5 92-41.414 92-92.5 92-92.5-41.19-92.5-92z" fill="#071e26" fill-rule="evenodd"/>
|
||||||
|
<g fill-rule="evenodd">
|
||||||
|
<path d="m558.39 229.38c-4.429-5.408-3.634-13.382 1.774-17.81s13.382-3.634 17.81 1.774 3.634 13.382-1.774 17.81-13.382 3.634-17.81-1.774z" fill="#fff"/>
|
||||||
|
<path d="m563.87 225.09c-4.43-5.411-3.635-13.388 1.775-17.818s13.388-3.636 17.818 1.775c4.43 5.41 3.636 13.387-1.775 17.817-5.41 4.431-13.387 3.636-17.818-1.774z" fill="#071e26"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#fff">
|
||||||
|
<g fill-rule="evenodd">
|
||||||
|
<path d="m620 220.8 2.795-1.1463 1.205-2.6566 1.205 2.6566 2.795 1.1463-2.795 1.1463-1.205 2.6566-1.205-2.6566z" stroke-width="1.0424"/>
|
||||||
|
<path d="m587 240 1.4963-0.603 0.64534-1.397 0.64534 1.397 1.4963 0.603-1.4963 0.603-0.64534 1.397-0.64534-1.397z" stroke-width="1.1949"/>
|
||||||
|
<path d="m571 251.16 1.397-0.55454 0.603-1.2857 0.603 1.2857 1.397 0.55454-1.397 0.55453-0.603 1.2857-0.603-1.2857z" stroke-width="1.1076"/>
|
||||||
|
</g>
|
||||||
|
<path d="m629.49 185 0.794 1.7 1.7 0.793-1.7 0.794-0.794 1.7-0.793-1.7-1.7-0.794 1.7-0.793z"/>
|
||||||
|
<path d="m611 198 0.634 1.36 1.36 0.635-1.36 0.634-0.634 1.36-0.635-1.36-1.36-0.634 1.36-0.635z"/>
|
||||||
|
<path transform="matrix(-1.4991 0 0 -1.5 760.18 285.22)" d="m70.493 18.813 0.3174 0.68 0.68 0.3174-0.68 0.3173-0.3174 0.68-0.3173-0.68-0.68-0.3173 0.68-0.3174z"/>
|
||||||
|
<path d="m547.5 207-0.476-1.02-1.02-0.476 1.02-0.476 0.476-1.02 0.476 1.02 1.02 0.476-1.02 0.476z"/>
|
||||||
|
<path d="m575 207-0.318-0.68-0.68-0.317 0.68-0.318 0.318-0.68 0.317 0.68 0.68 0.318-0.68 0.317z"/>
|
||||||
|
<path transform="matrix(-1.8151 0 0 -2 602.88 248.81)" d="m35.247 9.4067 0.1586 0.34 0.34 0.15866-0.34 0.15867-0.1586 0.34-0.1587-0.34-0.34-0.15867 0.34-0.15866z"/>
|
||||||
|
<path d="m533 265-0.634-1.36-1.36-0.635 1.36-0.634 0.634-1.36 0.635 1.36 1.36 0.634-1.36 0.635z"/>
|
||||||
|
<path transform="matrix(1.2 0 0 1.143 372.51 135.24)" d="m176.23 47.033 0.794 1.7 1.7 0.7934-1.7 0.7933-0.794 1.7-0.793-1.7-1.7-0.7933 1.7-0.7934z"/>
|
||||||
|
<path transform="matrix(1,0,0,1.2,374.26,190.56)" d="m176.23 47.033 0.794 1.7 1.7 0.7934-1.7 0.7933-0.794 1.7-0.793-1.7-1.7-0.7933 1.7-0.7934z"/>
|
||||||
|
<path transform="matrix(1.2843 0 0 1.5 579.75 197.78)" d="m70.493 18.813 0.3174 0.68 0.68 0.3174-0.68 0.3173-0.3174 0.68-0.3173-0.68-0.68-0.3173 0.68-0.3174z"/>
|
||||||
|
<path transform="matrix(-1.2 0 0 -1.1072 887.49 304.61)" d="m176.23 47.033 0.794 1.7 1.7 0.7934-1.7 0.7933-0.794 1.7-0.793-1.7-1.7-0.7933 1.7-0.7934z"/>
|
||||||
|
<path d="m646.49 235 0.794 1.7 1.7 0.793-1.7 0.794-0.794 1.7-0.793-1.7-1.7-0.794 1.7-0.793z"/>
|
||||||
|
<path d="m652.49 205 0.794 1.7 1.7 0.793-1.7 0.794-0.794 1.7-0.793-1.7-1.7-0.794 1.7-0.793z"/>
|
||||||
|
<path d="m590.99 213 0.952 2.04 2.04 0.952-2.04 0.952-0.952 2.04-0.952-2.04-2.04-0.952 2.04-0.952z"/>
|
||||||
|
<path d="m562 196-0.318-0.68-0.68-0.317 0.68-0.318 0.318-0.68 0.317 0.68 0.68 0.318-0.68 0.317z"/>
|
||||||
|
<path d="m629 245-0.3172-0.63469-0.68-0.29616 0.68-0.29618 0.3172-0.63469 0.3174 0.63469 0.68 0.29618-0.68 0.29616z" stroke-width="1.9322"/>
|
||||||
|
<path d="m603.51 184-0.794-1.7-1.7-0.793 1.7-0.794 0.794-1.7 0.793 1.7 1.7 0.794-1.7 0.793z"/>
|
||||||
|
</g>
|
||||||
|
<g fill-rule="evenodd">
|
||||||
|
<path d="m532.55 294.65 14.287-13.336 3.834 1.008 16-16.628 9.5 7.222 14.666-17.3 4 0.336 12.834-14.948 11.166 8.902 6.5 9.238 4.167-1.512 14.667 18.139 10.166-7.726 7 5.711 3 4.367 4.167-1.008 15.5 17.132-75.333 11.757z" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
|
||||||
|
<path d="m608.81 246 1.003 8.209-1.839 2.849 0.836 12.9-6.35-8.544-4.011 9.047 7.353 11.057-0.668 6.702 5.013 7.036-8.021-7.706-4.345-2.178-3.676-5.864-8.69-9.215-7.52 5.529-10.861-6.534 0.334 6.869-4.011 2.01 0.836 4.356-3.008 0.503 1.002 3.686-7.5127 6.7787 56.141 16.509 71.187-16.586-13.368-13.404-3.008 0.671-5.849-6.534-4.679-2.513c0.056 1.34 0.111 2.68 0.167 4.02l1.504 4.692-3.509-1.173v3.853l3.008 7.037-27.238-28.984-3.844 1.507-7.185-9.717z" fill="#071e26"/>
|
||||||
|
<path d="m557.76 295.44 7.1017-6.2644 20.228-15.095 1.8517 2.4251-3.1978 2.3216 1.189 3.166-4.588 5.334 2.039-0.334v3.167l-6.117 6.5z" fill="#fff" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#254711" fill-rule="evenodd">
|
||||||
|
<path d="m615 255 8.211 18.535 5.194 10.278-3.519 1.011 11.563 16.176 9.551-2.865-7.708-10.615 2.346-2.19-5.53-5.729-5.8039-11.523-3.0701 1.2994z"/>
|
||||||
|
<path d="m661 279 2.667 5.5-1.167 0.833 7 8.667 7.5-0.167-7.667-7.166-0.166-2.334-0.38281-1.3574-2.2842 0.85742z"/>
|
||||||
|
<path d="m572.72 279.92-0.977-5.277 4.808 2.384z"/>
|
||||||
|
<path d="m590 281 5.085 11.5-3.56 0.5 5.763 9.833 12.933 4.7511-4.6275-8.0841 0.678-2.333-4.068-1 1.017-1.5-4.576-6-2.034-0.167z"/>
|
||||||
|
</g>
|
||||||
|
<g fill-rule="evenodd">
|
||||||
|
<path d="m527 299.96c18.547-4.603 31.068-3.672 53.401 1.668s69.294 26.311 80.599 30.371c-12.862 13.31-54.572 64.019-54.619 64.003" fill="#335918"/>
|
||||||
|
<path d="m525.5 298.73c18.734-4.58 37.609-1.135 53.942 1.66s32.304 11.898 44.058 15.108" fill="none" stroke="#254711" stroke-miterlimit="8" stroke-width="7"/>
|
||||||
|
<path d="m546 324.3c11.029-3.094 36.132 4.084 59.023-15.243 7.76-3.544 34.236-10.913 48.394-13 14.158-2.088 35.759-5.911 36.555 0.473 0.797 6.384-15.639 20.615-28.285 36.832-13.976 16.715-43.816 47.263-55.073 59.64" fill="#335918"/>
|
||||||
|
<path d="m626.11 302.21c-1.294 0.057-23.877 5.2528-23.602 9.0858 0.276 3.833 29.829 7.526 24.789 13.7-5.041 6.173-51.588 9.809-55.031 23.34-3.443 13.53 26.684 37.26 32.827 43.462 6.142 6.201 13.494-8.5744 11.704-15.029-6.3872-3.715-28.859-12.535-20.899-25.558s37.1-13.22 46.107-23.509c0.083-12.995-29.003-12.46-31.73-16.744-2.726-4.285 17.13-8.8038 15.835-8.7478z" fill="#fff"/>
|
||||||
|
<path d="m669.28 193.75c36.135 36.135 36.606 94.249 1.054 129.8-22.955 22.955-45.516 47.442-65.428 73.44-20.337-26.324-41.044-51.163-64.374-74.493-36.135-36.135-36.606-94.249-1.053-129.8s93.667-35.081 129.8 1.053z" fill="none" stroke="#11db6d" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="9.3335"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<metadata>
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<cc:license rdf:resource="http://creativecommons.org/licenses/by/4.0/"/>
|
||||||
|
<dc:title>LiveTrail</dc:title>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Franzz</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License rdf:about="http://creativecommons.org/licenses/by/4.0/">
|
||||||
|
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
||||||
|
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
||||||
|
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
||||||
|
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
||||||
|
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 14 KiB |
@@ -1,32 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
|
||||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
|
||||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
|
||||||
preserveAspectRatio="xMidYMid meet">
|
|
||||||
<metadata>
|
|
||||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
|
||||||
</metadata>
|
|
||||||
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
|
||||||
fill="#000000" stroke="none">
|
|
||||||
<path d="M5041 5784 c366 -384 572 -817 630 -1323 17 -156 7 -495 -20 -641
|
|
||||||
-78 -415 -250 -717 -1091 -1915 -108 -154 -338 -482 -510 -730 -172 -247 -328
|
|
||||||
-469 -346 -492 -21 -25 -56 -52 -91 -68 -48 -23 -66 -27 -127 -23 -46 2 -86
|
|
||||||
11 -113 24 l-42 21 36 -44 c47 -58 122 -116 191 -150 50 -25 68 -28 152 -28
|
|
||||||
88 0 100 2 163 34 80 40 167 121 254 236 102 137 967 1375 1158 1660 429 639
|
|
||||||
578 959 636 1365 20 140 17 485 -5 630 -37 237 -101 440 -205 655 -162 331
|
|
||||||
-404 620 -696 830 -33 24 -28 16 26 -41z"/>
|
|
||||||
<path d="M2750 5673 c-63 -22 -143 -75 -197 -131 -192 -196 -318 -616 -298
|
|
||||||
-990 9 -174 36 -314 118 -607 l34 -120 316 -3 317 -2 0 62 c1 81 22 199 49
|
|
||||||
265 11 29 42 87 69 130 143 230 186 366 185 588 -1 420 -140 736 -356 809 -60
|
|
||||||
20 -177 20 -237 -1z"/>
|
|
||||||
<path d="M4017 5056 c-174 -62 -301 -280 -348 -591 -19 -129 -17 -337 4 -425
|
|
||||||
32 -136 85 -257 159 -368 81 -119 127 -268 128 -410 l0 -62 317 2 316 3 34
|
|
||||||
120 c106 374 136 590 115 816 -22 225 -66 391 -153 566 -48 97 -74 138 -131
|
|
||||||
198 -39 42 -90 87 -115 102 -99 58 -241 79 -326 49z"/>
|
|
||||||
<path d="M2412 3548 c3 -109 7 -129 30 -173 120 -232 445 -231 559 0 28 58 32
|
|
||||||
76 37 180 l5 115 -318 0 -317 0 4 -122z"/>
|
|
||||||
<path d="M3960 2962 c0 -234 106 -368 300 -380 98 -5 168 20 233 83 75 75 91
|
|
||||||
119 95 263 l4 122 -316 0 -316 0 0 -88z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,22 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "Spotty",
|
"name": "LiveTrail",
|
||||||
"short_name": "Spotty",
|
"short_name": "LiveTrail",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/images/icons/android-chrome-192x192.png?v=GvmqYyKwbb",
|
"src": "/assets/images/icons/web-app-manifest-192x192.png?v=20260528",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any maskable"
|
"purpose": "maskable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/icons/android-chrome-512x512.png?v=GvmqYyKwbb",
|
"src": "/assets/images/icons/web-app-manifest-512x512.png?v=20260528",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any maskable"
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#ffffff",
|
"theme_color": "#081B19",
|
||||||
"background_color": "#ffffff",
|
"background_color": "#081B19",
|
||||||
"display": "browser",
|
"display": "standalone"
|
||||||
"orientation": "portrait"
|
|
||||||
}
|
}
|
||||||
BIN
src/images/icons/web-app-manifest-192x192.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/images/icons/web-app-manifest-512x512.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
src/images/logo_bg.webp
Normal file
|
After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
33
src/images/logo_title.svg
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="394.17" height="113.24" overflow="hidden" version="1.1" viewBox="0 0 394.17 113.24" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||||
|
<title>LiveTrail</title>
|
||||||
|
<g transform="translate(-417.76 -414.44)">
|
||||||
|
<g transform="translate(0 .3998)">
|
||||||
|
<path d="m501.57 514.18h-5.1953v12.822h-1.9336v-12.822h-5.1953v-1.7188h12.324zm13.076 12.822h-1.9336v-7.1191h-7.2559v7.1191h-1.9336v-14.541h1.9336v5.7031h7.2559v-5.7031h1.9336zm13.486 0h-9.5801v-14.541h9.5801v1.7188h-7.6465v3.9844h7.6465v1.7188h-7.6465v5.4004h7.6465zm15.098 0h-2.5098l-4.8633-5.7812h-2.7246v5.7812h-1.9336v-14.541h4.0723q1.3184 0 2.1973 0.17579 0.87891 0.16601 1.582 0.60546 0.79101 0.49805 1.2305 1.2598 0.44922 0.75195 0.44922 1.9141 0 1.5723-0.79101 2.6367-0.79102 1.0547-2.1777 1.5918zm-4.5215-10.449q0-0.625-0.22461-1.1035-0.21484-0.48829-0.72266-0.82032-0.41992-0.2832-0.99609-0.39062-0.57617-0.11719-1.3574-0.11719h-2.2754v5.4883h1.9531q0.91797 0 1.6016-0.15625 0.6836-0.16601 1.1621-0.60547 0.43946-0.41015 0.64454-0.9375 0.21484-0.53711 0.21484-1.3574zm15.977 10.449h-9.5801v-14.541h9.5801v1.7188h-7.6465v3.9844h7.6465v1.7188h-7.6465v5.4004h7.6465zm21.562 0h-2.0606l-1.4258-4.0527h-6.2891l-1.4258 4.0527h-1.9629l5.293-14.541h2.5781zm-4.082-5.7129-2.5488-7.1387-2.5586 7.1387zm17.383 5.7129h-2.3926l-6.8945-13.008v13.008h-1.8066v-14.541h2.998l6.2891 11.875v-11.875h1.8066zm16.152-7.2559q0 1.9824-0.86914 3.5938-0.85938 1.6113-2.2949 2.5-0.99609 0.61523-2.2266 0.88867-1.2207 0.27344-3.2226 0.27344h-3.6719v-14.541h3.6328q2.1289 0 3.3789 0.3125 1.2598 0.30274 2.1289 0.83985 1.4844 0.92773 2.3144 2.4707t0.83008 3.6621zm-2.0215-0.0293q0-1.709-0.5957-2.8809t-1.7773-1.8457q-0.85938-0.48828-1.8262-0.67383-0.96679-0.19531-2.3144-0.19531h-1.8164v11.221h1.8164q1.3965 0 2.4316-0.20507 1.0449-0.20508 1.9141-0.76172 1.084-0.69336 1.6211-1.8262 0.54687-1.1328 0.54687-2.832zm23.145 2.8125q0 1.084-0.41015 1.9141-0.41016 0.83007-1.1035 1.3672-0.82031 0.64453-1.8066 0.91797-0.97656 0.27344-2.4902 0.27344h-5.1562v-14.541h4.3066q1.5918 0 2.3828 0.11719t1.5137 0.48828q0.80078 0.41992 1.1621 1.084 0.36133 0.65429 0.36133 1.5723 0 1.0352-0.52734 1.7676-0.52735 0.72266-1.4062 1.1621v0.0781q1.4746 0.30273 2.3242 1.2988 0.84961 0.98633 0.84961 2.5zm-3.252-6.5527q0-0.52734-0.17578-0.88867t-0.56641-0.58594q-0.45898-0.26367-1.1133-0.32227-0.6543-0.0684-1.6211-0.0684h-2.3047v4.1992h2.5q0.9082 0 1.4453-0.0879 0.53711-0.0976 0.9961-0.39062 0.45898-0.29297 0.64453-0.75196 0.19531-0.46875 0.19531-1.1035zm1.2402 6.6309q0-0.87891-0.26367-1.3965-0.26367-0.51757-0.95703-0.8789-0.46875-0.24414-1.1426-0.3125-0.66406-0.0781-1.6211-0.0781h-3.0371v5.4102h2.5586q1.2695 0 2.0801-0.12695 0.81055-0.13672 1.3281-0.48829 0.54687-0.38085 0.80078-0.86914 0.2539-0.48828 0.2539-1.2598zm16.221 4.3945h-2.0606l-1.4258-4.0527h-6.2891l-1.4258 4.0527h-1.9629l5.293-14.541h2.5781zm-4.082-5.7129-2.5488-7.1387-2.5586 7.1387zm17.52 4.6582q-0.53711 0.23438-0.97656 0.43946-0.42969 0.20507-1.1328 0.42968-0.5957 0.18555-1.2988 0.3125-0.69335 0.13672-1.5332 0.13672-1.582 0-2.8809-0.43945-1.2891-0.44922-2.2461-1.3965-0.9375-0.92773-1.4648-2.3535-0.52734-1.4356-0.52734-3.3301 0-1.7969 0.50781-3.2129 0.50782-1.416 1.4648-2.3926 0.92773-0.94726 2.2363-1.4453 1.3184-0.49805 2.9199-0.49805 1.1719 0 2.334 0.28321 1.1719 0.2832 2.5977 0.99609v2.2949h-0.14649q-1.2012-1.0059-2.3828-1.4648-1.1816-0.45899-2.5293-0.45899-1.1035 0-1.9922 0.36133-0.87891 0.35156-1.5723 1.1035-0.67383 0.73242-1.0547 1.8555-0.37109 1.1133-0.37109 2.5781 0 1.5332 0.41016 2.6367 0.41992 1.1035 1.0742 1.7969 0.6836 0.72266 1.5918 1.0742 0.91797 0.3418 1.9336 0.3418 1.3965 0 2.6172-0.47852 1.2207-0.47851 2.2852-1.4355h0.13672zm14.424 1.0547h-2.5098l-5.752-6.4746-1.4453 1.543v4.9316h-1.9336v-14.541h1.9336v7.5879l7.0606-7.5879h2.3438l-6.4941 6.8359zm20.664 0h-2.0606l-1.4258-4.0527h-6.2891l-1.4258 4.0527h-1.9629l5.293-14.541h2.5781zm-4.082-5.7129-2.5488-7.1387-2.5586 7.1387zm18.418 4.6387q-1.1914 0.54688-2.6074 0.95703-1.4062 0.40039-2.7246 0.40039-1.6992 0-3.1152-0.46875t-2.4121-1.4062q-1.0059-0.94726-1.5527-2.3633-0.54688-1.4258-0.54688-3.3301 0-3.4863 2.0312-5.498 2.041-2.0215 5.5957-2.0215 1.2402 0 2.5293 0.30274 1.2988 0.29297 2.793 1.0059v2.2949h-0.17578q-0.30274-0.23438-0.87891-0.61524t-1.1328-0.63476q-0.67383-0.30274-1.5332-0.49805-0.84961-0.20508-1.9336-0.20508-2.4414 0-3.8672 1.5723-1.416 1.5625-1.416 4.2383 0 2.8223 1.4844 4.3945 1.4844 1.5625 4.043 1.5625 0.9375 0 1.8652-0.18555 0.9375-0.18554 1.6406-0.47851v-3.5644h-3.8965v-1.6992h5.8106zm14.844 1.0742h-2.0606l-1.4258-4.0527h-6.2891l-1.4258 4.0527h-1.9629l5.293-14.541h2.5781zm-4.082-5.7129-2.5488-7.1387-2.5586 7.1387zm11.416 5.7129h-5.7422v-1.4844h1.9043v-11.572h-1.9043v-1.4844h5.7422v1.4844h-1.9043v11.572h1.9043zm14.385 0h-2.3926l-6.8945-13.008v13.008h-1.8066v-14.541h2.998l6.2891 11.875v-11.875h1.8066z" fill="#335918" aria-label="THERE AND BACK AGAIN"/>
|
||||||
|
<path d="m417.76 519.74h52.865" fill="none" fill-rule="evenodd" stroke="#335918" stroke-miterlimit="8" stroke-width="2.1593"/>
|
||||||
|
<path d="m759.07 519.74h52.865" fill="none" fill-rule="evenodd" stroke="#335918" stroke-miterlimit="8" stroke-width="2.1593"/>
|
||||||
|
<path d="m462.24 492.87h-44.479v-74.688h16.823v61.042h27.656zm16.771-61.771q-4.1667 0-6.8229-2.4479-2.6563-2.5-2.6563-6.0938 0-3.6979 2.6563-6.0417 2.6562-2.3438 6.8229-2.3438 4.2188 0 6.8229 2.3438 2.6563 2.3438 2.6563 6.0417 0 3.75-2.6563 6.1458-2.6042 2.3958-6.8229 2.3958zm8.125 61.771h-16.458v-53.334h16.458zm64.636-53.334-19.844 53.334h-18.75l-18.906-53.334h17.604l9.2709 32.865q1.5625 5.5729 1.8229 9.4792h0.20833q0.36458-3.6979 1.9271-9.1667l9.4792-33.177zm53.906 31.354h-34.792q0.83333 11.615 14.635 11.615 8.8021 0 15.469-4.1667v11.875q-7.3959 3.9584-19.219 3.9584-12.917 0-20.052-7.1354-7.1354-7.1875-7.1354-20 0-13.281 7.7084-21.042 7.7084-7.7604 18.958-7.7604 11.667 0 18.021 6.9271 6.4063 6.9271 6.4063 18.802zm-15.26-10.104q0-11.458-9.2709-11.458-3.9583 0-6.875 3.2813-2.8646 3.2813-3.4896 8.1771z" fill="#335918" aria-label="Live"/>
|
||||||
|
<path d="m664.9 432.01h-21.302v60.99h-16.875v-60.99h-21.198v-13.698h59.375zm35.208 22.5q-2.9688-1.6146-6.9271-1.6146-5.3646 0-8.3854 3.9583-3.0208 3.9063-3.0208 10.677v25.469h-16.458v-53.334h16.458v9.8959h0.20833q3.9063-10.833 14.063-10.833 2.6042 0 4.0625 0.625zm52.031 38.49h-15.573v-7.6563h-0.20833q-5.3646 8.9584-15.885 8.9584-7.7604 0-12.24-4.375-4.4271-4.4271-4.4271-11.771 0-15.521 18.385-17.917l14.479-1.9271q0-8.75-9.4792-8.75-9.5313 0-18.125 5.6771v-12.396q3.4375-1.7708 9.375-3.125 5.9896-1.3542 10.885-1.3542 22.813 0 22.813 22.76zm-15.469-21.667v-3.5938l-9.6875 1.25q-8.0209 1.0417-8.0209 7.2396 0 2.8125 1.9271 4.6354 1.9792 1.7708 5.3125 1.7708 4.6354 0 7.5521-3.1771 2.9167-3.2292 2.9167-8.125zm36.823-40.104q-4.1667 0-6.8229-2.4479-2.6563-2.5-2.6563-6.0938 0-3.6979 2.6563-6.0417t6.8229-2.3438q4.2188 0 6.8229 2.3438 2.6563 2.3438 2.6563 6.0417 0 3.75-2.6563 6.1458-2.6042 2.3958-6.8229 2.3958zm8.125 61.771h-16.458v-53.334h16.458zm30.313 0h-16.458v-78.959h16.458z" fill="#11db6d" aria-label="Trail"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<metadata>
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<dc:title>LiveTrail</dc:title>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Franzz</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<cc:license rdf:resource="http://creativecommons.org/licenses/by/4.0/"/>
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License rdf:about="http://creativecommons.org/licenses/by/4.0/">
|
||||||
|
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
|
||||||
|
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
|
||||||
|
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/>
|
||||||
|
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/>
|
||||||
|
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 261 KiB |
@@ -1,23 +1,23 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
viewBox="0 0 32 64"
|
viewBox="0 0 32 64"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="svg4"
|
id="svg4"
|
||||||
sodipodi:docname="footprint_mapbox.svg"
|
sodipodi:docname="footprint_mapbox.svg"
|
||||||
width="32"
|
width="32"
|
||||||
height="64"
|
height="64"
|
||||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
|
||||||
inkscape:export-filename="C:\Users\francois\Downloads\footprint_mapbox.png"
|
|
||||||
inkscape:export-xdpi="96"
|
inkscape:export-xdpi="96"
|
||||||
inkscape:export-ydpi="96">
|
inkscape:export-ydpi="96"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<title
|
||||||
|
id="title1">Logo Marker</title>
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata10">
|
id="metadata10">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
@@ -26,15 +26,35 @@
|
|||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by/4.0/" />
|
||||||
|
<dc:title>Logo Marker</dc:title>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Franzz</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
</cc:License>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
<defs
|
<defs
|
||||||
id="defs8">
|
id="defs8">
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="linearGradient4520"
|
id="linearGradient4520"
|
||||||
osb:paint="solid">
|
inkscape:swatch="solid">
|
||||||
<stop
|
<stop
|
||||||
style="stop-color:#6dff58;stop-opacity:1;"
|
style="stop-color:#6dff58;stop-opacity:1;"
|
||||||
offset="0"
|
offset="0"
|
||||||
@@ -75,7 +95,11 @@
|
|||||||
<filter
|
<filter
|
||||||
style="color-interpolation-filters:sRGB;"
|
style="color-interpolation-filters:sRGB;"
|
||||||
inkscape:label="Drop Shadow"
|
inkscape:label="Drop Shadow"
|
||||||
id="filter5223">
|
id="filter5223"
|
||||||
|
x="-0.12000002"
|
||||||
|
y="-0.090000001"
|
||||||
|
width="1.2900001"
|
||||||
|
height="1.2175">
|
||||||
<feFlood
|
<feFlood
|
||||||
flood-opacity="0.498039"
|
flood-opacity="0.498039"
|
||||||
flood-color="rgb(0,0,0)"
|
flood-color="rgb(0,0,0)"
|
||||||
@@ -114,24 +138,27 @@
|
|||||||
guidetolerance="10"
|
guidetolerance="10"
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="3840"
|
||||||
inkscape:window-height="1017"
|
inkscape:window-height="2054"
|
||||||
id="namedview6"
|
id="namedview6"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:snap-grids="true"
|
inkscape:snap-grids="true"
|
||||||
inkscape:zoom="7.375"
|
inkscape:zoom="7.375"
|
||||||
inkscape:cx="2.1125062"
|
inkscape:cx="2.0338983"
|
||||||
inkscape:cy="47.406997"
|
inkscape:cy="47.389831"
|
||||||
inkscape:window-x="-8"
|
inkscape:window-x="2549"
|
||||||
inkscape:window-y="-8"
|
inkscape:window-y="-11"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg4"
|
inkscape:current-layer="svg4"
|
||||||
units="px" />
|
units="px"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1" />
|
||||||
<path
|
<path
|
||||||
id="path4698"
|
id="path4698"
|
||||||
d="M 14.766749,31.354369 C 5.685625,18.189432 4,16.838309 4,11.999995 4,5.3725591 9.372562,1.6977696e-7 15.999999,1.6977696e-7 22.627436,1.6977696e-7 27.999997,5.3725591 27.999997,11.999995 c 0,4.838314 -1.685625,6.189437 -10.766748,19.354374 -0.595937,0.860873 -1.870625,0.860809 -2.4665,0 z"
|
d="M 14.766749,31.35437 C 5.6856259,18.189433 4.0000018,16.83831 4.0000018,11.999996 4.0000018,5.37256 9.3725628,0 15.999999,0 c 6.627437,0 11.999999,5.37256 11.999999,11.999996 0,4.838314 -1.685625,6.189437 -10.766749,19.354374 -0.595937,0.860873 -1.870625,0.860809 -2.4665,0 z"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="fill:#6dff58;fill-opacity:1;stroke:none;stroke-width:0.06249999;stroke-opacity:1;filter:url(#filter5223)"
|
style="fill:#6dff58;fill-opacity:1;stroke:none;stroke-width:0.0625;stroke-opacity:1;filter:url(#filter5223)"
|
||||||
inkscape:label="marker" />
|
inkscape:label="marker" />
|
||||||
<path
|
<path
|
||||||
id="shoes"
|
id="shoes"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 6.0 KiB |
293
src/images/source/logo_icon_inkscape.svg
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="192.8484"
|
||||||
|
height="239.95819"
|
||||||
|
overflow="hidden"
|
||||||
|
version="1.1"
|
||||||
|
id="svg87"
|
||||||
|
sodipodi:docname="logo_icon_inkscape.svg"
|
||||||
|
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<title
|
||||||
|
id="title2">LiveTrail</title>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview87"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2"
|
||||||
|
inkscape:cx="-147.25"
|
||||||
|
inkscape:cy="393.75"
|
||||||
|
inkscape:window-width="3840"
|
||||||
|
inkscape:window-height="2054"
|
||||||
|
inkscape:window-x="2549"
|
||||||
|
inkscape:window-y="-11"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g87" />
|
||||||
|
<defs
|
||||||
|
id="defs15" />
|
||||||
|
<g
|
||||||
|
id="g87"
|
||||||
|
inkscape:label="Logo"
|
||||||
|
transform="translate(-518.42466,-161.70322)">
|
||||||
|
<g
|
||||||
|
id="g1"
|
||||||
|
inkscape:label="Spot"
|
||||||
|
transform="translate(9.9387949)">
|
||||||
|
<path
|
||||||
|
d="m 512,258 c 0,-50.81 41.414,-92 92.5,-92 51.086,0 92.5,41.19 92.5,92 0,50.81 -41.414,92 -92.5,92 -51.086,0 -92.5,-41.19 -92.5,-92 z"
|
||||||
|
fill="#071e26"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path16"
|
||||||
|
inkscape:label="Night" />
|
||||||
|
<g
|
||||||
|
id="g88"
|
||||||
|
inkscape:label="Stars">
|
||||||
|
<g
|
||||||
|
id="g89"
|
||||||
|
inkscape:label="Moon">
|
||||||
|
<path
|
||||||
|
d="m 558.386,229.381 c -4.429,-5.408 -3.634,-13.382 1.774,-17.81 5.408,-4.428 13.382,-3.634 17.81,1.774 4.428,5.408 3.634,13.382 -1.774,17.81 -5.408,4.428 -13.382,3.634 -17.81,-1.774 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path77"
|
||||||
|
inkscape:label="Moon light" />
|
||||||
|
<path
|
||||||
|
d="m 563.87,225.086 c -4.43,-5.411 -3.635,-13.388 1.775,-17.818 5.41,-4.43 13.388,-3.636 17.818,1.775 4.43,5.41 3.636,13.387 -1.775,17.817 -5.41,4.431 -13.387,3.636 -17.818,-1.774 z"
|
||||||
|
fill="#071e26"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path78"
|
||||||
|
inkscape:label="Moon shade" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="m 620,220.80298 2.795,-1.14633 L 624,217 l 1.205,2.65665 2.795,1.14633 -2.795,1.14633 -1.205,2.65665 -1.205,-2.65665 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path37"
|
||||||
|
style="stroke-width:1.04238" />
|
||||||
|
<path
|
||||||
|
d="m 587,240 1.49627,-0.603 0.64534,-1.397 0.64534,1.397 1.49627,0.603 -1.49627,0.603 -0.64534,1.397 -0.64534,-1.397 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path38"
|
||||||
|
style="stroke-width:1.19488" />
|
||||||
|
<path
|
||||||
|
d="m 571,251.15974 1.397,-0.55454 0.603,-1.28573 0.603,1.28573 1.397,0.55454 -1.397,0.55453 L 573,253 l -0.603,-1.28573 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path74"
|
||||||
|
style="stroke-width:1.10763" />
|
||||||
|
<path
|
||||||
|
d="m 629.493,185 0.794,1.7 1.7,0.793 -1.7,0.794 -0.794,1.7 -0.793,-1.7 -1.7,-0.794 1.7,-0.793 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path68" />
|
||||||
|
<path
|
||||||
|
d="m 610.995,198 0.634,1.36 1.36,0.635 -1.36,0.634 -0.634,1.36 -0.635,-1.36 -1.36,-0.634 1.36,-0.635 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path65" />
|
||||||
|
<path
|
||||||
|
d="m 70.4933,18.8133 0.3174,0.68 0.68,0.3174 -0.68,0.3173 -0.3174,0.68 -0.3173,-0.68 -0.68,-0.3173 0.68,-0.3174 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
transform="matrix(-1.4991172,0,0,-1.5,760.1782,285.22)"
|
||||||
|
id="path62" />
|
||||||
|
<path
|
||||||
|
d="m 547.504,207 -0.476,-1.02 -1.02,-0.476 1.02,-0.476 0.476,-1.02 0.476,1.02 1.02,0.476 -1.02,0.476 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path59" />
|
||||||
|
<path
|
||||||
|
d="m 575.003,207 -0.318,-0.68 -0.68,-0.317 0.68,-0.318 0.318,-0.68 0.317,0.68 0.68,0.318 -0.68,0.317 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path53" />
|
||||||
|
<path
|
||||||
|
d="m 35.2467,9.40667 0.1586,0.34 0.34,0.15866 -0.34,0.15867 -0.1586,0.34 -0.1587,-0.34 -0.34,-0.15867 0.34,-0.15866 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
transform="matrix(-1.8150551,0,0,-2,602.88239,248.813)"
|
||||||
|
id="path47" />
|
||||||
|
<path
|
||||||
|
d="m 533.005,265 -0.634,-1.36 -1.36,-0.635 1.36,-0.634 0.634,-1.36 0.635,1.36 1.36,0.634 -1.36,0.635 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path44" />
|
||||||
|
<path
|
||||||
|
d="m 176.233,47.0333 0.794,1.7 1.7,0.7934 -1.7,0.7933 -0.794,1.7 -0.793,-1.7 -1.7,-0.7933 1.7,-0.7934 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
transform="matrix(1.2,0,0,1.1429601,372.512,135.24312)"
|
||||||
|
id="path41" />
|
||||||
|
<path
|
||||||
|
d="m 176.233,47.0333 0.794,1.7 1.7,0.7934 -1.7,0.7933 -0.794,1.7 -0.793,-1.7 -1.7,-0.7933 1.7,-0.7934 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
transform="matrix(1,0,0,1.2,374.26,190.56)"
|
||||||
|
id="path39" />
|
||||||
|
<path
|
||||||
|
d="m 70.4933,18.8133 0.3174,0.68 0.68,0.3174 -0.68,0.3173 -0.3174,0.68 -0.3173,-0.68 -0.68,-0.3173 0.68,-0.3174 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
transform="matrix(1.2843073,0,0,1.5,579.74578,197.78)"
|
||||||
|
id="path34" />
|
||||||
|
<path
|
||||||
|
d="m 176.233,47.0333 0.794,1.7 1.7,0.7934 -1.7,0.7933 -0.794,1.7 -0.793,-1.7 -1.7,-0.7933 1.7,-0.7934 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
transform="matrix(-1.2,0,0,-1.1072201,887.488,304.6106)"
|
||||||
|
id="path31" />
|
||||||
|
<path
|
||||||
|
d="m 646.493,235 0.794,1.7 1.7,0.793 -1.7,0.794 -0.794,1.7 -0.793,-1.7 -1.7,-0.794 1.7,-0.793 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path25" />
|
||||||
|
<path
|
||||||
|
d="m 652.493,205 0.794,1.7 1.7,0.793 -1.7,0.794 -0.794,1.7 -0.793,-1.7 -1.7,-0.794 1.7,-0.793 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path23" />
|
||||||
|
<path
|
||||||
|
d="m 590.992,213 0.952,2.04 2.04,0.952 -2.04,0.952 -0.952,2.04 -0.952,-2.04 -2.04,-0.952 2.04,-0.952 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path75" />
|
||||||
|
<path
|
||||||
|
d="m 562.003,196 -0.318,-0.68 -0.68,-0.317 0.68,-0.318 0.318,-0.68 0.317,0.68 0.68,0.318 -0.68,0.317 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path56" />
|
||||||
|
<path
|
||||||
|
d="m 629.0026,245.00033 -0.3172,-0.63469 -0.68,-0.29616 0.68,-0.29618 0.3172,-0.63469 0.3174,0.63469 0.68,0.29618 -0.68,0.29616 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path50"
|
||||||
|
style="stroke-width:1.9322" />
|
||||||
|
<path
|
||||||
|
d="m 603.507,184 -0.794,-1.7 -1.7,-0.793 1.7,-0.794 0.794,-1.7 0.793,1.7 1.7,0.794 -1.7,0.793 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
id="path28" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="M 532.54631,294.64628 546.833,281.31 l 3.834,1.008 16,-16.628 9.5,7.222 14.666,-17.3 4,0.336 12.834,-14.948 11.166,8.902 6.5,9.238 4.167,-1.512 14.667,18.139 10.166,-7.726 7,5.711 3,4.367 4.167,-1.008 L 684,294.243 608.667,306 Z"
|
||||||
|
stroke="#ffffff"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-miterlimit="10"
|
||||||
|
fill="#ffffff"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path18"
|
||||||
|
inkscape:label="Mountain Snow"
|
||||||
|
sodipodi:nodetypes="ccccccccccccccccccc" />
|
||||||
|
<path
|
||||||
|
d="m 608.808,246 1.003,8.209 -1.839,2.849 0.836,12.9 -6.35,-8.544 -4.011,9.047 7.353,11.057 -0.668,6.702 5.013,7.036 -8.021,-7.706 -4.345,-2.178 -3.676,-5.864 -8.69,-9.215 -7.52,5.529 -10.861,-6.534 0.334,6.869 -4.011,2.01 0.836,4.356 -3.008,0.503 1.002,3.686 -7.51266,6.77871 L 610.813,310 682,293.414 668.632,280.01 l -3.008,0.671 -5.849,-6.534 -4.679,-2.513 c 0.056,1.34 0.111,2.68 0.167,4.02 l 1.504,4.692 -3.509,-1.173 v 3.853 l 3.008,7.037 -27.238,-28.984 -3.844,1.507 -7.185,-9.717 z"
|
||||||
|
fill="#071e26"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path19"
|
||||||
|
inkscape:label="Mountain shade night"
|
||||||
|
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccc" />
|
||||||
|
<path
|
||||||
|
d="m 557.75677,295.43542 7.10172,-6.26435 20.22823,-15.09546 1.85166,2.42506 -3.19779,2.32165 1.189,3.166 -4.588,5.334 2.039,-0.334 v 3.167 l -6.117,6.5 z"
|
||||||
|
stroke="#ffffff"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-miterlimit="10"
|
||||||
|
fill="#ffffff"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path79"
|
||||||
|
inkscape:label="Mountain Snow 2"
|
||||||
|
sodipodi:nodetypes="ccccccccccc" />
|
||||||
|
<path
|
||||||
|
d="m 615,255 8.211,18.535 5.194,10.278 -3.519,1.011 11.563,16.176 9.551,-2.865 -7.708,-10.615 2.346,-2.19 -5.53,-5.729 -5.80393,-11.52332 -3.07013,1.29941 z"
|
||||||
|
fill="#254711"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path80"
|
||||||
|
sodipodi:nodetypes="cccccccccccc" />
|
||||||
|
<path
|
||||||
|
d="m 661,279 2.667,5.5 -1.167,0.833 7,8.667 7.5,-0.167 -7.667,-7.166 -0.166,-2.334 -0.38281,-1.35742 L 666.5,283.833 Z"
|
||||||
|
fill="#254711"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path81"
|
||||||
|
sodipodi:nodetypes="cccccccccc" />
|
||||||
|
<path
|
||||||
|
d="m 572.725,279.924 -0.977,-5.277 4.808,2.384 z"
|
||||||
|
fill="#254711"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path82" />
|
||||||
|
<path
|
||||||
|
d="m 590,281 5.085,11.5 -3.56,0.5 5.763,9.833 12.93254,4.75112 -4.62754,-8.08412 0.678,-2.333 -4.068,-1 1.017,-1.5 -4.576,-6 -2.034,-0.167 z"
|
||||||
|
fill="#254711"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path83"
|
||||||
|
sodipodi:nodetypes="cccccccccccc"
|
||||||
|
inkscape:label="Mountain shade 2" />
|
||||||
|
<path
|
||||||
|
d="m 527,299.958 c 18.547,-4.603 31.068,-3.672 53.401,1.668 22.333,5.34 69.294,26.311 80.599,30.371 -12.862,13.31 -54.572,64.019 -54.619,64.003"
|
||||||
|
fill="#335918"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path17"
|
||||||
|
inkscape:label="Hill left" />
|
||||||
|
<path
|
||||||
|
d="m 525.5,298.732 c 18.734,-4.58 37.609,-1.135 53.942,1.66 16.333,2.795 32.304,11.898 44.058,15.108"
|
||||||
|
stroke="#254711"
|
||||||
|
stroke-width="7"
|
||||||
|
stroke-miterlimit="8"
|
||||||
|
fill="none"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path84"
|
||||||
|
inkscape:label="Hill left over" />
|
||||||
|
<path
|
||||||
|
d="m 546,324.298 c 11.029,-3.094 36.132,4.084 59.023,-15.243 7.76,-3.544 34.236,-10.913 48.394,-13 14.158,-2.088 35.759,-5.911 36.555,0.473 0.797,6.384 -15.639,20.615 -28.285,36.832 -13.976,16.715 -43.816,47.263 -55.073,59.64"
|
||||||
|
fill="#335918"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path85"
|
||||||
|
inkscape:label="Hill right" />
|
||||||
|
<path
|
||||||
|
d="m 626.10529,302.21424 c -1.294,0.057 -23.87729,5.25276 -23.60229,9.08576 0.276,3.833 29.829,7.526 24.789,13.7 -5.041,6.173 -51.588,9.809 -55.031,23.34 -3.443,13.53 26.6842,37.26016 32.8272,43.46216 6.142,6.201 13.49409,-8.57436 11.70409,-15.02936 -6.3872,-3.71496 -28.85929,-12.5348 -20.89929,-25.5578 7.96,-13.023 37.1,-13.22 46.107,-23.509 0.083,-12.995 -29.003,-12.46 -31.73,-16.744 -2.726,-4.285 17.13029,-8.80376 15.83529,-8.74776 z"
|
||||||
|
fill="#ffffff"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path86"
|
||||||
|
sodipodi:nodetypes="ccccscsccc"
|
||||||
|
inkscape:label="Road" />
|
||||||
|
<path
|
||||||
|
d="m 669.284,193.753 c 36.135,36.135 36.606,94.249 1.054,129.802 -22.955,22.955 -45.51609,47.44205 -65.42809,73.44005 C 584.57291,370.67105 563.866,345.832 540.536,322.502 504.401,286.367 503.93,228.253 539.483,192.7 c 35.553,-35.553 93.667,-35.081 129.801,1.053 z"
|
||||||
|
stroke="#11db6d"
|
||||||
|
stroke-width="9.33333"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-miterlimit="10"
|
||||||
|
fill="none"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path87"
|
||||||
|
inkscape:label="Marker"
|
||||||
|
sodipodi:nodetypes="sccsss" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<metadata
|
||||||
|
id="metadata2">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:title>LiveTrail</dc:title>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Franzz</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |