main beautify
All checks were successful
Build, Push, Publish / Build & Release (push) Successful in 10m11s
383
.gitea/workflows/release_build.yml
Normal file
@@ -0,0 +1,383 @@
|
||||
name: Build, Push, Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '28 5 * * *'
|
||||
# workflow_run support in Gitea can be tricky, keeping it but might need adjustment
|
||||
workflow_run:
|
||||
workflows: ["Sync Repo"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build & Release
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: 📥 Checkout code with full history and tags
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for Dockerargs and Upstream Updates
|
||||
id: check_upstream
|
||||
run: |
|
||||
if [ -f Dockerargs ]; then
|
||||
echo "Dockerargs found. Checking upstream..."
|
||||
echo "Dockerargs found. Checking upstream..."
|
||||
# Parse repo info using awk to avoid git config restrictions on underscores in keys elsewhere in the file
|
||||
REPO_URL=$(awk -F '=' '/\[repo\]/{flag=1; next} /\[/{flag=0} flag && /^url=/{print $2}' Dockerargs | tr -d ' \r\n')
|
||||
REPO_BRANCH=$(awk -F '=' '/\[repo\]/{flag=1; next} /\[/{flag=0} flag && /^branch=/{print $2}' Dockerargs | tr -d ' \r\n')
|
||||
if [ -z "$REPO_BRANCH" ]; then REPO_BRANCH="main"; fi
|
||||
|
||||
# Fetch upstream SHA
|
||||
if [ -n "$REPO_URL" ]; then
|
||||
UPSTREAM_SHA=$(git ls-remote "$REPO_URL" "$REPO_BRANCH" | awk '{ print $1 }' | head -c 7)
|
||||
echo "Upstream SHA: $UPSTREAM_SHA"
|
||||
|
||||
if [ -f manifest.json ]; then
|
||||
LOCAL_SHA=$(jq -r '.upstream_sha // empty' manifest.json)
|
||||
else
|
||||
LOCAL_SHA=""
|
||||
fi
|
||||
|
||||
if [ "$LOCAL_SHA" != "$UPSTREAM_SHA" ]; then
|
||||
echo "Upstream changed ($LOCAL_SHA -> $UPSTREAM_SHA)."
|
||||
echo "upstream_needs_update=true" >> "$GITHUB_OUTPUT"
|
||||
echo "upstream_sha=$UPSTREAM_SHA" >> "$GITHUB_OUTPUT"
|
||||
echo "repo_url=$REPO_URL" >> "$GITHUB_OUTPUT"
|
||||
echo "repo_branch=$REPO_BRANCH" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Upstream up to date."
|
||||
echo "upstream_needs_update=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# Parse Build Args
|
||||
echo "Parsing [args] from Dockerargs..."
|
||||
ARGS_CONTENT=$(sed -n '/^\[args\]/,/^\[/p' Dockerargs | grep -v '^\[' | grep '=' || true)
|
||||
if [ -n "$ARGS_CONTENT" ]; then
|
||||
echo "Found args:"
|
||||
echo "$ARGS_CONTENT"
|
||||
echo "build_args<<EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "$ARGS_CONTENT" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "No args found."
|
||||
echo "build_args=" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
else
|
||||
echo "Repo URL not found in Dockerargs."
|
||||
echo "upstream_needs_update=false" >> "$GITHUB_OUTPUT"
|
||||
echo "build_args=" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
else
|
||||
echo "No Dockerargs found."
|
||||
echo "upstream_needs_update=false" >> "$GITHUB_OUTPUT"
|
||||
echo "build_args=" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Check if any tags exist
|
||||
id: check_tags_exist
|
||||
run: |
|
||||
git fetch --tags
|
||||
TAG_COUNT=$(git tag | wc -l)
|
||||
if [ "$TAG_COUNT" -eq 0 ]; then
|
||||
echo "has_tags=false" >> "$GITHUB_OUTPUT"
|
||||
echo "latest_tag=v0.0.0" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_tags=true" >> "$GITHUB_OUTPUT"
|
||||
LATEST_TAG=$(git describe --tags --abbrev=0)
|
||||
echo "latest_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Check if meaningful commits exist since latest tag
|
||||
id: check_commits
|
||||
run: |
|
||||
UPSTREAM_UPDATE="${{ steps.check_upstream.outputs.upstream_needs_update }}"
|
||||
if [ "$UPSTREAM_UPDATE" == "true" ]; then
|
||||
echo "commit_count=1" >> "$GITHUB_OUTPUT"
|
||||
echo "changed_files=Upstream Update to ${{ steps.check_upstream.outputs.upstream_sha }}" >> "$GITHUB_OUTPUT"
|
||||
elif [ "${{ steps.check_tags_exist.outputs.has_tags }}" = "false" ]; then
|
||||
# No tags exist, so we should create first release
|
||||
echo "commit_count=1" >> "$GITHUB_OUTPUT"
|
||||
CHANGED_FILES=$(git ls-files | grep -v '^manifest.json$' || true)
|
||||
if [ -n "$CHANGED_FILES" ]; then
|
||||
echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
|
||||
printf '%s\n' "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "changed_files=Initial release" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
else
|
||||
LATEST_TAG="${{ steps.check_tags_exist.outputs.latest_tag }}"
|
||||
CHANGED_FILES="$(git diff --name-only "${LATEST_TAG}..HEAD" | grep -v '^manifest.json$' || true)"
|
||||
if [ -n "$CHANGED_FILES" ]; then
|
||||
echo "commit_count=1" >> "$GITHUB_OUTPUT"
|
||||
echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
|
||||
printf '%s\n' "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "commit_count=0" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Get latest release tag (from Gitea API)
|
||||
id: get_latest_release
|
||||
run: |
|
||||
# Using Gitea API
|
||||
LATEST_RELEASE_TAG=$(curl -sL -H "Accept: application/json" \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
"${{ gitea.api_url }}/repos/${{ gitea.repository }}/releases/latest" | jq -r .tag_name)
|
||||
|
||||
if [ -z "$LATEST_RELEASE_TAG" ] || [ "$LATEST_RELEASE_TAG" = "null" ]; then
|
||||
LATEST_RELEASE_TAG="v1.0.0"
|
||||
fi
|
||||
echo "latest_release_tag=$LATEST_RELEASE_TAG" >> "$GITHUB_OUTPUT"
|
||||
echo "latest_release_version=${LATEST_RELEASE_TAG#v}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# -------------------------------
|
||||
# Sync manifest.json to last release version if behind (only when no meaningful commits)
|
||||
# -------------------------------
|
||||
- name: 🛠 Ensure manifest.json matches latest release version
|
||||
if: steps.check_commits.outputs.commit_count == '0'
|
||||
run: |
|
||||
if [ -f manifest.json ]; then
|
||||
MANIFEST_VERSION=$(jq -r '.version // empty' manifest.json)
|
||||
else
|
||||
MANIFEST_VERSION=""
|
||||
fi
|
||||
LATEST_RELEASE_VERSION="${{ steps.get_latest_release.outputs.latest_release_version }}"
|
||||
PYTHON_CODE="from packaging import version; \
|
||||
print(version.parse('$LATEST_RELEASE_VERSION') > version.parse('$MANIFEST_VERSION') if '$MANIFEST_VERSION' else True)"
|
||||
# Python3 is available in catthehacker/ubuntu:act-latest
|
||||
NEED_UPDATE=$(python3 -c "$PYTHON_CODE")
|
||||
if [ "$NEED_UPDATE" = "True" ]; then
|
||||
echo "Updating manifest.json to version $LATEST_RELEASE_VERSION (sync with release)"
|
||||
jq --arg v "$LATEST_RELEASE_VERSION" '.version = $v' manifest.json > tmp.json && mv tmp.json manifest.json
|
||||
git config user.name "Gitea Actions"
|
||||
git config user.email "actions@git.icc.gg"
|
||||
git add manifest.json
|
||||
git commit -m "Sync manifest.json to release $LATEST_RELEASE_VERSION [🔄]" || echo "Nothing to commit"
|
||||
git push origin main || true
|
||||
else
|
||||
echo "Manifest.json is already up-to-date with the latest release."
|
||||
fi
|
||||
|
||||
# -------------------------------
|
||||
# Continue normal workflow if commits exist
|
||||
# -------------------------------
|
||||
- name: 📃 Get list of changed files (Markdown bullet list)
|
||||
if: steps.check_commits.outputs.commit_count != '0'
|
||||
id: changed_files
|
||||
run: |
|
||||
BULLET_LIST="$(printf '%s\n' "${{ steps.check_commits.outputs.changed_files }}" | sed 's/^/- /')"
|
||||
echo "CHANGED<<EOF" >> "$GITHUB_OUTPUT"
|
||||
printf '%s\n' "$BULLET_LIST" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
COUNT="$(printf '%s\n' "${{ steps.check_commits.outputs.changed_files }}" | wc -l)"
|
||||
echo "COUNT=$COUNT" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Get manifest version
|
||||
if: steps.check_commits.outputs.commit_count != '0'
|
||||
id: get_manifest_version
|
||||
run: |
|
||||
if [ -f manifest.json ]; then
|
||||
MANIFEST_VERSION=$(jq -r '.version // empty' manifest.json)
|
||||
if [ -z "$MANIFEST_VERSION" ] || [ "$MANIFEST_VERSION" = "null" ]; then
|
||||
MANIFEST_VERSION="1.0.0"
|
||||
fi
|
||||
else
|
||||
MANIFEST_VERSION="1.0.0"
|
||||
fi
|
||||
echo "manifest_version=$MANIFEST_VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Pick base version
|
||||
if: steps.check_commits.outputs.commit_count != '0'
|
||||
id: pick_base_version
|
||||
run: |
|
||||
LATEST_RELEASE="${{ steps.get_latest_release.outputs.latest_release_version }}"
|
||||
MANIFEST="${{ steps.get_manifest_version.outputs.manifest_version }}"
|
||||
BASE_VERSION=$(python3 -c "from packaging import version; \
|
||||
print(str(max(version.parse('$LATEST_RELEASE'), version.parse('$MANIFEST'))))")
|
||||
echo "base_version=$BASE_VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 🔢 Determine version
|
||||
if: steps.check_commits.outputs.commit_count != '0'
|
||||
id: version
|
||||
run: |
|
||||
BASE_VERSION="${{ steps.pick_base_version.outputs.base_version }}"
|
||||
MAJOR=$(echo "$BASE_VERSION" | cut -d. -f1)
|
||||
MINOR=$(echo "$BASE_VERSION" | cut -d. -f2)
|
||||
PATCH=$(echo "$BASE_VERSION" | cut -d. -f3)
|
||||
COUNT="${{ steps.changed_files.outputs.COUNT }}"
|
||||
if [ "$COUNT" -ge 5 ]; then
|
||||
MAJOR=$((MAJOR + 1))
|
||||
MINOR=0
|
||||
PATCH=0
|
||||
elif [ "$COUNT" -ge 3 ]; then
|
||||
MINOR=$((MINOR + 1))
|
||||
PATCH=0
|
||||
else
|
||||
PATCH=$((PATCH + 1))
|
||||
fi
|
||||
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
|
||||
REPO_NAME="$(basename "$GITHUB_REPOSITORY")"
|
||||
ZIP_NAME="${REPO_NAME}-${NEW_VERSION}.zip"
|
||||
echo "VERSION=$NEW_VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "ZIP_NAME=$ZIP_NAME" >> "$GITHUB_OUTPUT"
|
||||
echo "REPO_NAME=$REPO_NAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 🛠 Update or create manifest.json
|
||||
if: steps.check_commits.outputs.commit_count != '0'
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.VERSION }}"
|
||||
AUTHOR="Ivan Carlos"
|
||||
VERSION_FILE="manifest.json"
|
||||
UPSTREAM_SHA="${{ steps.check_upstream.outputs.upstream_sha }}"
|
||||
|
||||
if [ -f "$VERSION_FILE" ]; then
|
||||
jq --arg v "$VERSION" \
|
||||
--arg a "$AUTHOR" \
|
||||
--arg u "$UPSTREAM_SHA" \
|
||||
'.version = $v | .author = $a | if $u != "" and $u != null then .upstream_sha = $u else . end' \
|
||||
"$VERSION_FILE" > tmp.json && mv tmp.json "$VERSION_FILE"
|
||||
else
|
||||
echo "{ \"version\": \"$VERSION\", \"author\": \"$AUTHOR\", \"upstream_sha\": \"$UPSTREAM_SHA\" }" > "$VERSION_FILE"
|
||||
fi
|
||||
|
||||
- name: 💾 Commit and push updated manifest.json
|
||||
if: steps.check_commits.outputs.commit_count != '0'
|
||||
run: |
|
||||
git config user.name "Gitea Actions"
|
||||
git config user.email "actions@git.icc.gg"
|
||||
git add manifest.json
|
||||
git commit -m "Update manifest version to ${{ steps.version.outputs.VERSION }} [▶️]" || echo "Nothing to commit"
|
||||
git push origin main
|
||||
|
||||
- name: 📦 Create ZIP package (excluding certain files)
|
||||
if: steps.check_commits.outputs.commit_count != '0'
|
||||
run: |
|
||||
ZIP_NAME="${{ steps.version.outputs.ZIP_NAME }}"
|
||||
zip -r "$ZIP_NAME" . -x ".git/*" ".github/*" "docker/*" ".dockerignore" "CNAME" "Dockerfile" "README.md" "LICENSE" ".gitea/*"
|
||||
|
||||
- name: 🚀 Create Gitea Release
|
||||
if: steps.check_commits.outputs.commit_count != '0'
|
||||
id: create_release
|
||||
env:
|
||||
CHANGELOG_LIST: ${{ steps.changed_files.outputs.CHANGED }}
|
||||
run: |
|
||||
TAG_NAME="v${{ steps.version.outputs.VERSION }}"
|
||||
RELEASE_NAME="${{ steps.version.outputs.REPO_NAME }} v${{ steps.version.outputs.VERSION }}"
|
||||
|
||||
# Construct Markdown body safely using env var
|
||||
# We use printf to avoid interpreting backslashes in the file list
|
||||
BODY=$(printf "### Changelog\nFiles changed in this release:\n%s" "$CHANGELOG_LIST")
|
||||
|
||||
# Create JSON payload using jq
|
||||
jq -n \
|
||||
--arg tag_name "$TAG_NAME" \
|
||||
--arg name "$RELEASE_NAME" \
|
||||
--arg body "$BODY" \
|
||||
'{tag_name: $tag_name, name: $name, body: $body, draft: false, prerelease: false}' > release_payload.json
|
||||
|
||||
echo "DEBUG: Generated Payload:"
|
||||
cat release_payload.json
|
||||
|
||||
# Create Release
|
||||
curl -s -X POST "${{ gitea.api_url }}/repos/${{ gitea.repository }}/releases" \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @release_payload.json > api_response.json
|
||||
|
||||
echo "DEBUG: API Response:"
|
||||
cat api_response.json || true
|
||||
|
||||
RELEASE_ID=$(jq -r .id api_response.json)
|
||||
echo "RELEASE_ID=$RELEASE_ID" >> "$GITHUB_OUTPUT"
|
||||
|
||||
if [ "$RELEASE_ID" == "null" ] || [ -z "$RELEASE_ID" ]; then
|
||||
echo "Failed to create release. Response content:"
|
||||
cat api_response.json
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 📤 Upload Release Asset
|
||||
if: steps.check_commits.outputs.commit_count != '0'
|
||||
run: |
|
||||
RELEASE_ID="${{ steps.create_release.outputs.RELEASE_ID }}"
|
||||
ZIP_NAME="${{ steps.version.outputs.ZIP_NAME }}"
|
||||
FILE_PATH="./$ZIP_NAME"
|
||||
|
||||
curl -s -X POST "${{ gitea.api_url }}/repos/${{ gitea.repository }}/releases/$RELEASE_ID/assets" \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "Content-Type: application/zip" \
|
||||
--data-binary @"$FILE_PATH" \
|
||||
-o /dev/null
|
||||
|
||||
# ----- Docker steps -----
|
||||
- name: Clone Upstream Code (if needed)
|
||||
if: steps.check_commits.outputs.commit_count != '0' && (steps.check_upstream.outputs.upstream_needs_update == 'true' || steps.check_upstream.outputs.repo_url != '')
|
||||
run: |
|
||||
rm -rf upstream_src
|
||||
git clone --depth 1 --branch ${{ steps.check_upstream.outputs.repo_branch }} ${{ steps.check_upstream.outputs.repo_url }} upstream_src
|
||||
|
||||
- name: 🔍 Check if Dockerfile exists
|
||||
if: steps.check_commits.outputs.commit_count != '0' || steps.check_upstream.outputs.upstream_needs_update == 'true'
|
||||
id: dockerfile_check
|
||||
run: |
|
||||
if [ -n "${{ steps.check_upstream.outputs.repo_url }}" ]; then
|
||||
if [ -f upstream_src/Dockerfile ]; then
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
# Fallback or error? User said "ignore", but we need a dockerfile to build.
|
||||
# Assuming if upstream_src is present, we trust it, or fail at build time.
|
||||
# Let's say exists=true and let build fail if missing, per user hint.
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
elif [ -f Dockerfile ]; then
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: 🔐 Login to Gitea Container Registry
|
||||
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.icc.gg
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.CR_PAT }}
|
||||
|
||||
- name: 🛠 Set up QEMU
|
||||
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: 🛠 Set up Docker Buildx
|
||||
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: 🐳 Build and Push Docker image
|
||||
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
|
||||
uses: docker/build-push-action@v5
|
||||
id: docker_build
|
||||
with:
|
||||
context: ${{ steps.check_upstream.outputs.repo_url != '' && './upstream_src' || '.' }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
file: ${{ steps.check_upstream.outputs.repo_url != '' && './upstream_src/Dockerfile' || './Dockerfile' }}
|
||||
push: true
|
||||
build-args: |
|
||||
${{ steps.check_upstream.outputs.build_args }}
|
||||
tags: |
|
||||
git.icc.gg/${{ gitea.repository }}:latest
|
||||
git.icc.gg/${{ gitea.repository }}:${{ steps.version.outputs.VERSION }}
|
||||
8
Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY public/ /usr/share/nginx/html
|
||||
|
||||
# Install wget and download jQuery
|
||||
RUN apk add --no-cache wget && \
|
||||
mkdir -p /usr/share/nginx/html/jquery && \
|
||||
wget https://code.jquery.com/jquery-3.5.1.slim.min.js -O /usr/share/nginx/html/jquery/jquery-3.5.1.slim.min.js
|
||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Ivan Carlos
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
4
manifest.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"author": "Ivan Carlos"
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/images/icons/icon-128x128.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/images/icons/icon-144x144.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
public/images/icons/icon-152x152.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/images/icons/icon-192x192.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
public/images/icons/icon-384x384.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
public/images/icons/icon-512x512.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
public/images/icons/icon-72x72.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/images/icons/icon-96x96.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
263
public/index.html
Normal file
@@ -0,0 +1,263 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="WiFi Code is a fully browser-based site for generating QR codes">
|
||||
<meta name="author" content="Ivan Carlos">
|
||||
<title>Wifi no QR</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" media="all" href="style.css">
|
||||
<link rel="stylesheet" type="text/css" media="print" href="print.css">
|
||||
<link rel="icon" href="qifi.png" type="image/x-icon">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<link rel="canonical" href="https://wifi.git.icc.gg/">
|
||||
<!-- Bootstrap removed -->
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<section id="generator" class="card">
|
||||
<header class="page-header">
|
||||
<h1 id="title">Wifi no QR</h1>
|
||||
<p class="subtitle">
|
||||
Enter your WiFi details below and click <strong>Generate</strong>. The QR Code is created locally in your browser—no data is sent to any server.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<!-- History Dropdown Placeholder (Hidden by default) -->
|
||||
<div id="history-drop" class="history-dropdown" style="display:none;">
|
||||
<button class="history-btn">Load History ▼</button>
|
||||
<ul class="dropdown-menu"></ul>
|
||||
</div>
|
||||
|
||||
<form id="form">
|
||||
<div class="form-group">
|
||||
<label for="ssid">SSID (Network Name)</label>
|
||||
<div class="input-wrapper">
|
||||
<!-- Signal Icon -->
|
||||
<svg class="input-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.55a11 11 0 0 1 14.08 0"></path><path d="M1.42 9a16 16 0 0 1 21.16 0"></path><path d="M8.53 16.11a6 6 0 0 1 6.95 0"></path><line x1="12" y1="20" x2="12.01" y2="20"></line></svg>
|
||||
<input type="text" id="ssid" name="ssid" placeholder="MyWiFiNetwork" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="enc">Encryption</label>
|
||||
<div class="input-wrapper">
|
||||
<select name="enc" id="enc">
|
||||
<option value="WPA">WPA/WPA2/WPA3</option>
|
||||
<option value="WEP">WEP</option>
|
||||
<option value="nopass">None</option>
|
||||
</select>
|
||||
<svg class="select-arrow" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="key-p">
|
||||
<label for="key">Password</label>
|
||||
<div class="input-wrapper">
|
||||
<!-- Lock Icon -->
|
||||
<svg class="input-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
|
||||
<input type="password" id="key" name="key" placeholder="Wireless Key" required>
|
||||
<button type="button" id="display-key" class="icon-btn">
|
||||
<!-- Eye Icon -->
|
||||
<svg id="display-key-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group checkbox-group">
|
||||
<input type="checkbox" name="hidden" id="hidden">
|
||||
<label for="hidden">Hidden Network</label>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button id="generate" class="btn btn-primary">Generate QR Code</button>
|
||||
<button id="save" type="button" class="btn btn-secondary" title="Save credentials locally">Save</button>
|
||||
<a href="#" id="export" class="btn btn-outline" target="_blank" title="Download PNG">Download PNG</a>
|
||||
<a href="javascript:window.print()" id="print" class="btn btn-outline" title="Print">Print</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="result-area">
|
||||
<h2 id="showssid">SSID: none</h2>
|
||||
<h3 id="showkey">Passphrase: none</h3>
|
||||
<div id="qrcode"></div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p><a href="https://icc.gg/privacidade" target="_blank">Privacy Policy</a> • <a href="https://icc.gg/termos" target="_blank">Terms and Conditions</a></p>
|
||||
</footer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Scripts preserved and paths adjusted if necessary -->
|
||||
<script src="jquery/jquery-3.5.1.slim.min.js"></script>
|
||||
<script src="jquery-qrcode/jquery.qrcode.min.js"></script>
|
||||
<script src="jquery.storage.js/jquery.storage.js"></script>
|
||||
|
||||
<!-- Original Logic Preserved -->
|
||||
<script type="text/javascript">
|
||||
function escape_string (string) {
|
||||
var to_escape = ['\\', ';', ',', ':', '"'];
|
||||
var hex_only = /^[0-9a-f]+$/i;
|
||||
var output = "";
|
||||
for (var i=0; i<string.length; i++) {
|
||||
if($.inArray(string[i], to_escape) != -1) {
|
||||
output += '\\'+string[i];
|
||||
}
|
||||
else {
|
||||
output += string[i];
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
function generate () {
|
||||
var ssid = $('#ssid').val();
|
||||
var hidden = $('#hidden').is(':checked');
|
||||
var enc = $('#enc').val();
|
||||
if (enc != 'nopass') {
|
||||
var key = $('#key').val();
|
||||
$('#showkey').text(enc+' Passphrase: '+key);
|
||||
} else {
|
||||
var key = '';
|
||||
$('#showkey').text('');
|
||||
}
|
||||
// https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11
|
||||
var qrstring = 'WIFI:S:'+escape_string(ssid)+';T:'+enc+';P:'+escape_string(key)+';';
|
||||
if (hidden) {
|
||||
qrstring += 'H:true';
|
||||
}
|
||||
qrstring += ';';
|
||||
$('#qrcode').empty();
|
||||
$('#qrcode').qrcode(qrstring);
|
||||
$('#showssid').text('SSID: '+ssid);
|
||||
$('#save').show();
|
||||
$('#print').css('display', 'inline-block');
|
||||
$('#showssid, #showkey').show(); // Show result text
|
||||
|
||||
var canvas = $('#qrcode canvas');
|
||||
if (canvas.length == 1) {
|
||||
var data = canvas[0].toDataURL('image/png');
|
||||
var e = $('#export');
|
||||
e.attr('href', data);
|
||||
e.attr('download', ssid+'-qrcode.png');
|
||||
e.css('display', 'inline-block');
|
||||
}
|
||||
};
|
||||
|
||||
function save () {
|
||||
var ssid = $('#ssid').val();
|
||||
if (!ssid) return;
|
||||
var hidden = $('#hidden').is(':checked');
|
||||
var enc = $('#enc').val();
|
||||
var key = $('#key').val();
|
||||
var storage = $.localStorage('qificodes');
|
||||
if (!storage) storage = {};
|
||||
storage[ssid] = {'hidden': hidden, 'enc': enc, 'key': key};
|
||||
$.localStorage('qificodes', storage);
|
||||
loadhistory();
|
||||
};
|
||||
|
||||
function load(ssid) {
|
||||
var storage = $.localStorage('qificodes');
|
||||
if (ssid in storage) {
|
||||
$('#ssid').val(ssid);
|
||||
$('#enc').val(storage[ssid]['enc']);
|
||||
$('#key').val(storage[ssid]['key']);
|
||||
$('#hidden').prop('checked', storage[ssid]['hidden']);
|
||||
generate();
|
||||
}
|
||||
};
|
||||
|
||||
function loadhistory () {
|
||||
var storage = $.localStorage('qificodes');
|
||||
if (storage) {
|
||||
var history = $('#history-drop ul.dropdown-menu');
|
||||
var ssids = Object.keys(storage);
|
||||
history.empty();
|
||||
for (var i=0; i<ssids.length; i++) {
|
||||
history.append('<li><a href="#" class="history-item">'+ssids[i]+'</a></li>');
|
||||
}
|
||||
history.append('<li class="divider"></li>');
|
||||
history.append('<li><a href="#" class="history-clear">clear history</a></li>');
|
||||
|
||||
history.on('click', 'a.history-item', function(e) {
|
||||
e.preventDefault();
|
||||
load($(this).text());
|
||||
});
|
||||
history.on('click', 'a.history-clear', function(e) {
|
||||
e.preventDefault();
|
||||
clearhistory();
|
||||
});
|
||||
$('#history-drop').show();
|
||||
}
|
||||
};
|
||||
|
||||
function clearhistory () {
|
||||
$.localStorage('qificodes', null);
|
||||
$('#history-drop').hide();
|
||||
};
|
||||
|
||||
$(document).ready(function(){
|
||||
$('#form').submit(function() {
|
||||
generate();
|
||||
// Scroll to result on mobile
|
||||
$('html, body').animate({
|
||||
scrollTop: $("#result-area").offset().top - 20
|
||||
}, 500);
|
||||
return false;
|
||||
});
|
||||
$('#save').click(function() {
|
||||
save();
|
||||
});
|
||||
|
||||
// Eye toggle logic adapted for SVG changes
|
||||
$('#display-key').click(function() {
|
||||
var $key = $("#key");
|
||||
var $icon = $("#display-key-icon");
|
||||
if ($key.attr('type') === 'password') {
|
||||
$key.attr('type', 'text');
|
||||
// Simple logic to visual indicate change - in real app would swap SVG path
|
||||
$icon.css('opacity', '0.5');
|
||||
} else {
|
||||
$key.attr('type', 'password');
|
||||
$icon.css('opacity', '1');
|
||||
}
|
||||
});
|
||||
|
||||
$('#enc').change(function() {
|
||||
if($(this).val() != 'nopass') {
|
||||
$('#key-p').slideDown();
|
||||
$('#key').attr('required','required');
|
||||
}
|
||||
else {
|
||||
$('#key-p').slideUp();
|
||||
$('#key').removeAttr('required');
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle history logic
|
||||
$('.history-btn').click(function() {
|
||||
$('.dropdown-menu').toggle();
|
||||
});
|
||||
|
||||
loadhistory();
|
||||
});
|
||||
|
||||
// Service Worker installation
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function() {
|
||||
navigator.serviceWorker.register('/sw.js', {scope: './'}).then(function(registration) {
|
||||
console.log('[Service Worker] Successfully installed');
|
||||
}).catch(function(error) {
|
||||
console.log('[Service Worker] Installation failed:', error);
|
||||
})
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1
public/jquery-qrcode/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bower_components
|
||||
20
public/jquery-qrcode/MIT-LICENSE.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2011 Jerome Etienne, http://jetienne.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
32
public/jquery-qrcode/Makefile
Normal file
@@ -0,0 +1,32 @@
|
||||
PROJECT_NAME=jquery-qrcode
|
||||
|
||||
all:
|
||||
|
||||
server:
|
||||
python -m SimpleHTTPServer
|
||||
|
||||
build: minify
|
||||
|
||||
minify:
|
||||
echo > /tmp/jquery.qrcode.tmp.js
|
||||
head -2 src/jquery.qrcode.js >> /tmp/jquery.qrcode.tmp.js
|
||||
cat src/qrcode.js >> /tmp/jquery.qrcode.tmp.js
|
||||
tail -n +3 src/jquery.qrcode.js >> /tmp/jquery.qrcode.tmp.js
|
||||
curl --data-urlencode "js_code@/tmp/jquery.qrcode.tmp.js" \
|
||||
-d "output_format=text&output_info=compiled_code&compilation_level=SIMPLE_OPTIMIZATIONS" \
|
||||
http://closure-compiler.appspot.com/compile \
|
||||
> jquery.qrcode.min.js
|
||||
|
||||
homepage_build:
|
||||
pandoc -A ~/.pandoc.header.html -s README.md -o index.html
|
||||
sed -i "s/github.com\/you/github.com\/jeromeetienne\/$(PROJECT_NAME)/g" index.html
|
||||
|
||||
#################################################################################
|
||||
# deploy #
|
||||
#################################################################################
|
||||
|
||||
deploy: build
|
||||
# assume there is something to commit
|
||||
# use "git diff --exit-code HEAD" to know if there is something to commit
|
||||
# so two lines: one if no commit, one if something to commit
|
||||
git commit -a -m "New deploy" && git push -f origin HEAD:gh-pages && git reset HEAD~
|
||||
26
public/jquery-qrcode/bower.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "jquery-qrcode",
|
||||
"version": "1.0.0",
|
||||
"homepage": "http://www.d-project.com/",
|
||||
"authors": [
|
||||
"jeromeetienne"
|
||||
],
|
||||
"description": "It allow you to easily add qrcode to your webpages. It is standalone, less than 4k after minify+gzip, no image download",
|
||||
"main": "jquery.qrcode.min.js",
|
||||
"keywords": [
|
||||
"QR",
|
||||
"jQuery",
|
||||
"standalone"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": "~1.9"
|
||||
}
|
||||
}
|
||||
29
public/jquery-qrcode/examples/basic.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>basic example</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
|
||||
|
||||
<!--<script type="text/javascript" src="../jquery.qrcode.min.js"></script>
|
||||
--><script type="text/javascript" src="../src/jquery.qrcode.js"></script>
|
||||
<script type="text/javascript" src="../src/qrcode.js"></script>
|
||||
|
||||
<p>Render in table</p>
|
||||
<div id="qrcodeTable"></div>
|
||||
<p>Render in canvas</p>
|
||||
<div id="qrcodeCanvas"></div>
|
||||
<script>
|
||||
//jQuery('#qrcode').qrcode("this plugin is great");
|
||||
jQuery('#qrcodeTable').qrcode({
|
||||
render : "table",
|
||||
text : "http://jetienne.com"
|
||||
});
|
||||
jQuery('#qrcodeCanvas').qrcode({
|
||||
text : "http://jetienne.com"
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
24
public/jquery-qrcode/examples/demo.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Demo page</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
TODO make a nice looking pure client qrcode generator
|
||||
even allow download of the image
|
||||
</p>
|
||||
|
||||
<div id="output"></div>
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../jquery.qrcode.min.js"></script>
|
||||
<script>
|
||||
jQuery(function(){
|
||||
jQuery('#output').qrcode("http://jetienne.com");
|
||||
})
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
28
public/jquery-qrcode/jquery.qrcode.min.js
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
(function(r){r.fn.qrcode=function(h){var s;function u(a){this.mode=s;this.data=a}function o(a,c){this.typeNumber=a;this.errorCorrectLevel=c;this.modules=null;this.moduleCount=0;this.dataCache=null;this.dataList=[]}function q(a,c){if(void 0==a.length)throw Error(a.length+"/"+c);for(var d=0;d<a.length&&0==a[d];)d++;this.num=Array(a.length-d+c);for(var b=0;b<a.length-d;b++)this.num[b]=a[b+d]}function p(a,c){this.totalCount=a;this.dataCount=c}function t(){this.buffer=[];this.length=0}u.prototype={getLength:function(){return this.data.length},
|
||||
write:function(a){for(var c=0;c<this.data.length;c++)a.put(this.data.charCodeAt(c),8)}};o.prototype={addData:function(a){this.dataList.push(new u(a));this.dataCache=null},isDark:function(a,c){if(0>a||this.moduleCount<=a||0>c||this.moduleCount<=c)throw Error(a+","+c);return this.modules[a][c]},getModuleCount:function(){return this.moduleCount},make:function(){if(1>this.typeNumber){for(var a=1,a=1;40>a;a++){for(var c=p.getRSBlocks(a,this.errorCorrectLevel),d=new t,b=0,e=0;e<c.length;e++)b+=c[e].dataCount;
|
||||
for(e=0;e<this.dataList.length;e++)c=this.dataList[e],d.put(c.mode,4),d.put(c.getLength(),j.getLengthInBits(c.mode,a)),c.write(d);if(d.getLengthInBits()<=8*b)break}this.typeNumber=a}this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17;this.modules=Array(this.moduleCount);for(var d=0;d<this.moduleCount;d++){this.modules[d]=Array(this.moduleCount);for(var b=0;b<this.moduleCount;b++)this.modules[d][b]=null}this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-
|
||||
7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(a,c);7<=this.typeNumber&&this.setupTypeNumber(a);null==this.dataCache&&(this.dataCache=o.createData(this.typeNumber,this.errorCorrectLevel,this.dataList));this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,c){for(var d=-1;7>=d;d++)if(!(-1>=a+d||this.moduleCount<=a+d))for(var b=-1;7>=b;b++)-1>=c+b||this.moduleCount<=c+b||(this.modules[a+d][c+b]=
|
||||
0<=d&&6>=d&&(0==b||6==b)||0<=b&&6>=b&&(0==d||6==d)||2<=d&&4>=d&&2<=b&&4>=b?!0:!1)},getBestMaskPattern:function(){for(var a=0,c=0,d=0;8>d;d++){this.makeImpl(!0,d);var b=j.getLostPoint(this);if(0==d||a>b)a=b,c=d}return c},createMovieClip:function(a,c,d){a=a.createEmptyMovieClip(c,d);this.make();for(c=0;c<this.modules.length;c++)for(var d=1*c,b=0;b<this.modules[c].length;b++){var e=1*b;this.modules[c][b]&&(a.beginFill(0,100),a.moveTo(e,d),a.lineTo(e+1,d),a.lineTo(e+1,d+1),a.lineTo(e,d+1),a.endFill())}return a},
|
||||
setupTimingPattern:function(){for(var a=8;a<this.moduleCount-8;a++)null==this.modules[a][6]&&(this.modules[a][6]=0==a%2);for(a=8;a<this.moduleCount-8;a++)null==this.modules[6][a]&&(this.modules[6][a]=0==a%2)},setupPositionAdjustPattern:function(){for(var a=j.getPatternPosition(this.typeNumber),c=0;c<a.length;c++)for(var d=0;d<a.length;d++){var b=a[c],e=a[d];if(null==this.modules[b][e])for(var f=-2;2>=f;f++)for(var i=-2;2>=i;i++)this.modules[b+f][e+i]=-2==f||2==f||-2==i||2==i||0==f&&0==i?!0:!1}},setupTypeNumber:function(a){for(var c=
|
||||
j.getBCHTypeNumber(this.typeNumber),d=0;18>d;d++){var b=!a&&1==(c>>d&1);this.modules[Math.floor(d/3)][d%3+this.moduleCount-8-3]=b}for(d=0;18>d;d++)b=!a&&1==(c>>d&1),this.modules[d%3+this.moduleCount-8-3][Math.floor(d/3)]=b},setupTypeInfo:function(a,c){for(var d=j.getBCHTypeInfo(this.errorCorrectLevel<<3|c),b=0;15>b;b++){var e=!a&&1==(d>>b&1);6>b?this.modules[b][8]=e:8>b?this.modules[b+1][8]=e:this.modules[this.moduleCount-15+b][8]=e}for(b=0;15>b;b++)e=!a&&1==(d>>b&1),8>b?this.modules[8][this.moduleCount-
|
||||
b-1]=e:9>b?this.modules[8][15-b-1+1]=e:this.modules[8][15-b-1]=e;this.modules[this.moduleCount-8][8]=!a},mapData:function(a,c){for(var d=-1,b=this.moduleCount-1,e=7,f=0,i=this.moduleCount-1;0<i;i-=2)for(6==i&&i--;;){for(var g=0;2>g;g++)if(null==this.modules[b][i-g]){var n=!1;f<a.length&&(n=1==(a[f]>>>e&1));j.getMask(c,b,i-g)&&(n=!n);this.modules[b][i-g]=n;e--; -1==e&&(f++,e=7)}b+=d;if(0>b||this.moduleCount<=b){b-=d;d=-d;break}}}};o.PAD0=236;o.PAD1=17;o.createData=function(a,c,d){for(var c=p.getRSBlocks(a,
|
||||
c),b=new t,e=0;e<d.length;e++){var f=d[e];b.put(f.mode,4);b.put(f.getLength(),j.getLengthInBits(f.mode,a));f.write(b)}for(e=a=0;e<c.length;e++)a+=c[e].dataCount;if(b.getLengthInBits()>8*a)throw Error("code length overflow. ("+b.getLengthInBits()+">"+8*a+")");for(b.getLengthInBits()+4<=8*a&&b.put(0,4);0!=b.getLengthInBits()%8;)b.putBit(!1);for(;!(b.getLengthInBits()>=8*a);){b.put(o.PAD0,8);if(b.getLengthInBits()>=8*a)break;b.put(o.PAD1,8)}return o.createBytes(b,c)};o.createBytes=function(a,c){for(var d=
|
||||
0,b=0,e=0,f=Array(c.length),i=Array(c.length),g=0;g<c.length;g++){var n=c[g].dataCount,h=c[g].totalCount-n,b=Math.max(b,n),e=Math.max(e,h);f[g]=Array(n);for(var k=0;k<f[g].length;k++)f[g][k]=255&a.buffer[k+d];d+=n;k=j.getErrorCorrectPolynomial(h);n=(new q(f[g],k.getLength()-1)).mod(k);i[g]=Array(k.getLength()-1);for(k=0;k<i[g].length;k++)h=k+n.getLength()-i[g].length,i[g][k]=0<=h?n.get(h):0}for(k=g=0;k<c.length;k++)g+=c[k].totalCount;d=Array(g);for(k=n=0;k<b;k++)for(g=0;g<c.length;g++)k<f[g].length&&
|
||||
(d[n++]=f[g][k]);for(k=0;k<e;k++)for(g=0;g<c.length;g++)k<i[g].length&&(d[n++]=i[g][k]);return d};s=4;for(var j={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,
|
||||
78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(a){for(var c=a<<10;0<=j.getBCHDigit(c)-j.getBCHDigit(j.G15);)c^=j.G15<<j.getBCHDigit(c)-j.getBCHDigit(j.G15);return(a<<10|c)^j.G15_MASK},getBCHTypeNumber:function(a){for(var c=a<<12;0<=j.getBCHDigit(c)-
|
||||
j.getBCHDigit(j.G18);)c^=j.G18<<j.getBCHDigit(c)-j.getBCHDigit(j.G18);return a<<12|c},getBCHDigit:function(a){for(var c=0;0!=a;)c++,a>>>=1;return c},getPatternPosition:function(a){return j.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,c,d){switch(a){case 0:return 0==(c+d)%2;case 1:return 0==c%2;case 2:return 0==d%3;case 3:return 0==(c+d)%3;case 4:return 0==(Math.floor(c/2)+Math.floor(d/3))%2;case 5:return 0==c*d%2+c*d%3;case 6:return 0==(c*d%2+c*d%3)%2;case 7:return 0==(c*d%3+(c+d)%2)%2;default:throw Error("bad maskPattern:"+
|
||||
a);}},getErrorCorrectPolynomial:function(a){for(var c=new q([1],0),d=0;d<a;d++)c=c.multiply(new q([1,l.gexp(d)],0));return c},getLengthInBits:function(a,c){if(1<=c&&10>c)switch(a){case 1:return 10;case 2:return 9;case s:return 8;case 8:return 8;default:throw Error("mode:"+a);}else if(27>c)switch(a){case 1:return 12;case 2:return 11;case s:return 16;case 8:return 10;default:throw Error("mode:"+a);}else if(41>c)switch(a){case 1:return 14;case 2:return 13;case s:return 16;case 8:return 12;default:throw Error("mode:"+
|
||||
a);}else throw Error("type:"+c);},getLostPoint:function(a){for(var c=a.getModuleCount(),d=0,b=0;b<c;b++)for(var e=0;e<c;e++){for(var f=0,i=a.isDark(b,e),g=-1;1>=g;g++)if(!(0>b+g||c<=b+g))for(var h=-1;1>=h;h++)0>e+h||c<=e+h||0==g&&0==h||i==a.isDark(b+g,e+h)&&f++;5<f&&(d+=3+f-5)}for(b=0;b<c-1;b++)for(e=0;e<c-1;e++)if(f=0,a.isDark(b,e)&&f++,a.isDark(b+1,e)&&f++,a.isDark(b,e+1)&&f++,a.isDark(b+1,e+1)&&f++,0==f||4==f)d+=3;for(b=0;b<c;b++)for(e=0;e<c-6;e++)a.isDark(b,e)&&!a.isDark(b,e+1)&&a.isDark(b,e+
|
||||
2)&&a.isDark(b,e+3)&&a.isDark(b,e+4)&&!a.isDark(b,e+5)&&a.isDark(b,e+6)&&(d+=40);for(e=0;e<c;e++)for(b=0;b<c-6;b++)a.isDark(b,e)&&!a.isDark(b+1,e)&&a.isDark(b+2,e)&&a.isDark(b+3,e)&&a.isDark(b+4,e)&&!a.isDark(b+5,e)&&a.isDark(b+6,e)&&(d+=40);for(e=f=0;e<c;e++)for(b=0;b<c;b++)a.isDark(b,e)&&f++;a=Math.abs(100*f/c/c-50)/5;return d+10*a}},l={glog:function(a){if(1>a)throw Error("glog("+a+")");return l.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;256<=a;)a-=255;return l.EXP_TABLE[a]},EXP_TABLE:Array(256),
|
||||
LOG_TABLE:Array(256)},m=0;8>m;m++)l.EXP_TABLE[m]=1<<m;for(m=8;256>m;m++)l.EXP_TABLE[m]=l.EXP_TABLE[m-4]^l.EXP_TABLE[m-5]^l.EXP_TABLE[m-6]^l.EXP_TABLE[m-8];for(m=0;255>m;m++)l.LOG_TABLE[l.EXP_TABLE[m]]=m;q.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var c=Array(this.getLength()+a.getLength()-1),d=0;d<this.getLength();d++)for(var b=0;b<a.getLength();b++)c[d+b]^=l.gexp(l.glog(this.get(d))+l.glog(a.get(b)));return new q(c,0)},mod:function(a){if(0>
|
||||
this.getLength()-a.getLength())return this;for(var c=l.glog(this.get(0))-l.glog(a.get(0)),d=Array(this.getLength()),b=0;b<this.getLength();b++)d[b]=this.get(b);for(b=0;b<a.getLength();b++)d[b]^=l.gexp(l.glog(a.get(b))+c);return(new q(d,0)).mod(a)}};p.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],
|
||||
[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,
|
||||
116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,
|
||||
43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,
|
||||
3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,
|
||||
55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,
|
||||
45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];p.getRSBlocks=function(a,c){var d=p.getRsBlockTable(a,c);if(void 0==d)throw Error("bad rs block @ typeNumber:"+a+"/errorCorrectLevel:"+c);for(var b=d.length/3,e=[],f=0;f<b;f++)for(var h=d[3*f+0],g=d[3*f+1],j=d[3*f+2],l=0;l<h;l++)e.push(new p(g,j));return e};p.getRsBlockTable=function(a,c){switch(c){case 1:return p.RS_BLOCK_TABLE[4*(a-1)+0];case 0:return p.RS_BLOCK_TABLE[4*(a-1)+1];case 3:return p.RS_BLOCK_TABLE[4*
|
||||
(a-1)+2];case 2:return p.RS_BLOCK_TABLE[4*(a-1)+3]}};t.prototype={get:function(a){return 1==(this.buffer[Math.floor(a/8)]>>>7-a%8&1)},put:function(a,c){for(var d=0;d<c;d++)this.putBit(1==(a>>>c-d-1&1))},getLengthInBits:function(){return this.length},putBit:function(a){var c=Math.floor(this.length/8);this.buffer.length<=c&&this.buffer.push(0);a&&(this.buffer[c]|=128>>>this.length%8);this.length++}};"string"===typeof h&&(h={text:h});h=r.extend({},{render:"canvas",width:256,height:256,typeNumber:-1,
|
||||
correctLevel:2,background:"#ffffff",foreground:"#000000"},h);return this.each(function(){var a;if("canvas"==h.render){a=new o(h.typeNumber,h.correctLevel);a.addData(h.text);a.make();var c=document.createElement("canvas");c.width=h.width;c.height=h.height;for(var d=c.getContext("2d"),b=h.width/a.getModuleCount(),e=h.height/a.getModuleCount(),f=0;f<a.getModuleCount();f++)for(var i=0;i<a.getModuleCount();i++){d.fillStyle=a.isDark(f,i)?h.foreground:h.background;var g=Math.ceil((i+1)*b)-Math.floor(i*b),
|
||||
j=Math.ceil((f+1)*b)-Math.floor(f*b);d.fillRect(Math.round(i*b),Math.round(f*e),g,j)}}else{a=new o(h.typeNumber,h.correctLevel);a.addData(h.text);a.make();c=r("<table></table>").css("width",h.width+"px").css("height",h.height+"px").css("border","0px").css("border-collapse","collapse").css("background-color",h.background);d=h.width/a.getModuleCount();b=h.height/a.getModuleCount();for(e=0;e<a.getModuleCount();e++){f=r("<tr></tr>").css("height",b+"px").appendTo(c);for(i=0;i<a.getModuleCount();i++)r("<td></td>").css("width",
|
||||
d+"px").css("background-color",a.isDark(e,i)?h.foreground:h.background).appendTo(f)}}a=c;jQuery(a).appendTo(this)})}})(jQuery);
|
||||
89
public/jquery-qrcode/src/jquery.qrcode.js
Normal file
@@ -0,0 +1,89 @@
|
||||
(function( $ ){
|
||||
$.fn.qrcode = function(options) {
|
||||
// if options is string,
|
||||
if( typeof options === 'string' ){
|
||||
options = { text: options };
|
||||
}
|
||||
|
||||
// set default values
|
||||
// typeNumber < 1 for automatic calculation
|
||||
options = $.extend( {}, {
|
||||
render : "canvas",
|
||||
width : 256,
|
||||
height : 256,
|
||||
typeNumber : -1,
|
||||
correctLevel : QRErrorCorrectLevel.H,
|
||||
background : "#ffffff",
|
||||
foreground : "#000000"
|
||||
}, options);
|
||||
|
||||
var createCanvas = function(){
|
||||
// create the qrcode itself
|
||||
var qrcode = new QRCode(options.typeNumber, options.correctLevel);
|
||||
qrcode.addData(options.text);
|
||||
qrcode.make();
|
||||
|
||||
// create canvas element
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = options.width;
|
||||
canvas.height = options.height;
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
// compute tileW/tileH based on options.width/options.height
|
||||
var tileW = options.width / qrcode.getModuleCount();
|
||||
var tileH = options.height / qrcode.getModuleCount();
|
||||
|
||||
// draw in the canvas
|
||||
for( var row = 0; row < qrcode.getModuleCount(); row++ ){
|
||||
for( var col = 0; col < qrcode.getModuleCount(); col++ ){
|
||||
ctx.fillStyle = qrcode.isDark(row, col) ? options.foreground : options.background;
|
||||
var w = (Math.ceil((col+1)*tileW) - Math.floor(col*tileW));
|
||||
var h = (Math.ceil((row+1)*tileH) - Math.floor(row*tileH));
|
||||
ctx.fillRect(Math.round(col*tileW),Math.round(row*tileH), w, h);
|
||||
}
|
||||
}
|
||||
// return just built canvas
|
||||
return canvas;
|
||||
}
|
||||
|
||||
// from Jon-Carlos Rivera (https://github.com/imbcmdth)
|
||||
var createTable = function(){
|
||||
// create the qrcode itself
|
||||
var qrcode = new QRCode(options.typeNumber, options.correctLevel);
|
||||
qrcode.addData(options.text);
|
||||
qrcode.make();
|
||||
|
||||
// create table element
|
||||
var $table = $('<table></table>')
|
||||
.css("width", options.width+"px")
|
||||
.css("height", options.height+"px")
|
||||
.css("border", "0px")
|
||||
.css("border-collapse", "collapse")
|
||||
.css('background-color', options.background);
|
||||
|
||||
// compute tileS percentage
|
||||
var tileW = options.width / qrcode.getModuleCount();
|
||||
var tileH = options.height / qrcode.getModuleCount();
|
||||
|
||||
// draw in the table
|
||||
for(var row = 0; row < qrcode.getModuleCount(); row++ ){
|
||||
var $row = $('<tr></tr>').css('height', tileH+"px").appendTo($table);
|
||||
|
||||
for(var col = 0; col < qrcode.getModuleCount(); col++ ){
|
||||
$('<td></td>')
|
||||
.css('width', tileW+"px")
|
||||
.css('background-color', qrcode.isDark(row, col) ? options.foreground : options.background)
|
||||
.appendTo($row);
|
||||
}
|
||||
}
|
||||
// return just built canvas
|
||||
return $table;
|
||||
}
|
||||
|
||||
|
||||
return this.each(function(){
|
||||
var element = options.render == "canvas" ? createCanvas() : createTable();
|
||||
$(element).appendTo(this);
|
||||
});
|
||||
};
|
||||
})( jQuery );
|
||||
1237
public/jquery-qrcode/src/qrcode.js
vendored
Normal file
22
public/jquery.storage.js/.gitattributes
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
*.sln merge=union
|
||||
*.csproj merge=union
|
||||
*.vbproj merge=union
|
||||
*.fsproj merge=union
|
||||
*.dbproj merge=union
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
215
public/jquery.storage.js/.gitignore
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
#################
|
||||
## Eclipse
|
||||
#################
|
||||
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
|
||||
#################
|
||||
## Visual Studio
|
||||
#################
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
*.pubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
#packages/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
#############
|
||||
## Windows detritus
|
||||
#############
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
|
||||
|
||||
#############
|
||||
## Python
|
||||
#############
|
||||
|
||||
*.py[co]
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist/
|
||||
build/
|
||||
eggs/
|
||||
parts/
|
||||
var/
|
||||
sdist/
|
||||
develop-eggs/
|
||||
.installed.cfg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
|
||||
#Translations
|
||||
*.mo
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
82
public/jquery.storage.js/jquery.storage.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/*!
|
||||
* jquery.storage.js 0.0.3 - https://github.com/yckart/jquery.storage.js
|
||||
* The client-side storage for every browser, on any device.
|
||||
*
|
||||
* Copyright (c) 2012 Yannick Albert (http://yckart.com)
|
||||
* Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php).
|
||||
* 2013/02/10
|
||||
**/
|
||||
;(function($, window, document) {
|
||||
'use strict';
|
||||
|
||||
$.map(['localStorage', 'sessionStorage'], function( method ) {
|
||||
var defaults = {
|
||||
cookiePrefix : 'fallback:' + method + ':',
|
||||
cookieOptions : {
|
||||
path : '/',
|
||||
domain : document.domain,
|
||||
expires : ('localStorage' === method) ? { expires: 365 } : undefined
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
$.support[method] = method in window && window[method] !== null;
|
||||
} catch (e) {
|
||||
$.support[method] = false;
|
||||
}
|
||||
|
||||
$[method] = function(key, value) {
|
||||
var options = $.extend({}, defaults, $[method].options);
|
||||
|
||||
this.getItem = function( key ) {
|
||||
var returns = function(key){
|
||||
return JSON.parse($.support[method] ? window[method].getItem(key) : $.cookie(options.cookiePrefix + key));
|
||||
};
|
||||
if(typeof key === 'string') return returns(key);
|
||||
|
||||
var arr = [],
|
||||
i = key.length;
|
||||
while(i--) arr[i] = returns(key[i]);
|
||||
return arr;
|
||||
};
|
||||
|
||||
this.setItem = function( key, value ) {
|
||||
value = JSON.stringify(value);
|
||||
return $.support[method] ? window[method].setItem(key, value) : $.cookie(options.cookiePrefix + key, value, options.cookieOptions);
|
||||
};
|
||||
|
||||
this.removeItem = function( key ) {
|
||||
return $.support[method] ? window[method].removeItem(key) : $.cookie(options.cookiePrefix + key, null, $.extend(options.cookieOptions, {
|
||||
expires: -1
|
||||
}));
|
||||
};
|
||||
|
||||
this.clear = function() {
|
||||
if($.support[method]) {
|
||||
return window[method].clear();
|
||||
} else {
|
||||
var reg = new RegExp('^' + options.cookiePrefix, ''),
|
||||
opts = $.extend(options.cookieOptions, {
|
||||
expires: -1
|
||||
});
|
||||
|
||||
if(document.cookie && document.cookie !== ''){
|
||||
$.map(document.cookie.split(';'), function( cookie ){
|
||||
if(reg.test(cookie = $.trim(cookie))) {
|
||||
$.cookie( cookie.substr(0,cookie.indexOf('=')), null, opts);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof key !== "undefined") {
|
||||
return typeof value !== "undefined" ? ( value === null ? this.removeItem(key) : this.setItem(key, value) ) : this.getItem(key);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
$[method].options = defaults;
|
||||
});
|
||||
}(jQuery, window, document));
|
||||
31
public/jquery.storage.js/localstorage.jquery.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "localstorage",
|
||||
"title": "jQuery Localstorage",
|
||||
"description": "The client-side storage for every browser, on any device.",
|
||||
"keywords": [
|
||||
"cookie",
|
||||
"storage",
|
||||
"localstorage",
|
||||
"save",
|
||||
"yckart",
|
||||
"yannick",
|
||||
"albert"
|
||||
],
|
||||
"version": "0.0.3",
|
||||
"author": {
|
||||
"name": "Yannick Albert",
|
||||
"email": "mail@yckart.com",
|
||||
"url": "https://github.com/yckart"
|
||||
},
|
||||
"licenses": [{
|
||||
"type": "MIT",
|
||||
"url": "http://yckart.com/mit/"
|
||||
}],
|
||||
"homepage": "http://yckart.github.com/jquery.storage.js",
|
||||
"demo": "http://yckart.github.com/jquery.storage.js",
|
||||
"docs": "https://github.com/yckart/jquery.storage.js",
|
||||
"bugs": "https://github.com/yckart/jquery.storage.js/issues",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.6.4"
|
||||
}
|
||||
}
|
||||
31
public/jquery.storage.js/sessionstorage.jquery.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "sessionstorage",
|
||||
"title": "jQuery Sessionstorage",
|
||||
"description": "The client-side storage for every browser, on any device.",
|
||||
"keywords": [
|
||||
"cookie",
|
||||
"storage",
|
||||
"localstorage",
|
||||
"save",
|
||||
"yckart",
|
||||
"yannick",
|
||||
"albert"
|
||||
],
|
||||
"version": "0.0.3",
|
||||
"author": {
|
||||
"name": "Yannick Albert",
|
||||
"email": "mail@yckart.com",
|
||||
"url": "https://github.com/yckart"
|
||||
},
|
||||
"licenses": [{
|
||||
"type": "MIT",
|
||||
"url": "http://yckart.com/mit/"
|
||||
}],
|
||||
"homepage": "http://yckart.github.com/jquery.storage.js",
|
||||
"demo": "http://yckart.github.com/jquery.storage.js",
|
||||
"docs": "https://github.com/yckart/jquery.storage.js",
|
||||
"bugs": "https://github.com/yckart/jquery.storage.js/issues",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.6.4"
|
||||
}
|
||||
}
|
||||
31
public/jquery.storage.js/storage.jquery.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "storage",
|
||||
"title": "jQuery Storage",
|
||||
"description": "The client-side storage for every browser, on any device.",
|
||||
"keywords": [
|
||||
"cookie",
|
||||
"storage",
|
||||
"localstorage",
|
||||
"save",
|
||||
"yckart",
|
||||
"yannick",
|
||||
"albert"
|
||||
],
|
||||
"version": "0.0.3",
|
||||
"author": {
|
||||
"name": "Yannick Albert",
|
||||
"email": "mail@yckart.com",
|
||||
"url": "https://github.com/yckart"
|
||||
},
|
||||
"licenses": [{
|
||||
"type": "MIT",
|
||||
"url": "http://yckart.com/mit/"
|
||||
}],
|
||||
"homepage": "http://yckart.github.com/jquery.storage.js",
|
||||
"demo": "http://yckart.github.com/jquery.storage.js",
|
||||
"docs": "https://github.com/yckart/jquery.storage.js",
|
||||
"bugs": "https://github.com/yckart/jquery.storage.js/issues",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.6.4"
|
||||
}
|
||||
}
|
||||
56
public/manifest.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "QiFi - WiFi QR Code Generator",
|
||||
"short_name": "QiFi",
|
||||
"theme_color": "#101010",
|
||||
"background_color": "#101010",
|
||||
"description": "WiFi no QR",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"start_url": "/",
|
||||
"Scope": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "images/icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/icons/icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/icons/icon-152x152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/icons/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "images/icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"splash_pages": null,
|
||||
"version": "1.1.39",
|
||||
"author": "Ivan Carlos"
|
||||
}
|
||||
17
public/print.css
Normal file
@@ -0,0 +1,17 @@
|
||||
.navbar,
|
||||
#generator .page-header,
|
||||
#about,
|
||||
#history,
|
||||
#form,
|
||||
#github,
|
||||
#contact,
|
||||
.footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#showssid,
|
||||
#showkey {
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
font-family: Consolas, Menlo, "DejaVu Sans Mono", monospace;
|
||||
}
|
||||
BIN
public/qifi-small.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/qifi.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
93
public/qifi.svg
Normal file
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
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"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="qifi.svg"
|
||||
inkscape:export-filename="/home/evgeni/Devel/qifi/qifi.png"
|
||||
inkscape:export-xdpi="6.9230771"
|
||||
inkscape:export-ydpi="6.9230771">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.23"
|
||||
inkscape:cx="316.09184"
|
||||
inkscape:cy="610.15534"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1411"
|
||||
inkscape:window-height="878"
|
||||
inkscape:window-x="29"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:16;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="rect3773"
|
||||
width="400"
|
||||
height="400"
|
||||
x="84"
|
||||
y="218.36218" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:16;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="rect2985"
|
||||
width="251.42857"
|
||||
height="251.42857"
|
||||
x="25.714001"
|
||||
y="292.93359" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:16;stroke-linecap:butt;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="rect2985-1"
|
||||
width="251.42857"
|
||||
height="251.42857"
|
||||
x="292.14288"
|
||||
y="292.93359" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:10px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans Italic"
|
||||
x="58.097557"
|
||||
y="486.50854"
|
||||
id="text3775"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3777"
|
||||
x="58.097557"
|
||||
y="486.50854"
|
||||
style="font-size:200px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans Bold"><tspan
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
id="tspan3779">Qi</tspan>Fi</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
2
public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
304
public/style.css
Normal file
@@ -0,0 +1,304 @@
|
||||
:root {
|
||||
--primary-color: #0070f3;
|
||||
--primary-hover: #0366d6;
|
||||
--bg-gradient-start: #f5f7fa;
|
||||
--bg-gradient-end: #c3cfe2;
|
||||
--card-bg: #ffffff;
|
||||
--text-main: #333;
|
||||
--text-muted: #666;
|
||||
--border-color: #eaeaea;
|
||||
--input-bg: #f9f9f9;
|
||||
--radius: 8px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%);
|
||||
color: var(--text-main);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding: 40px 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg);
|
||||
padding: 40px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h1#title {
|
||||
margin: 0 0 10px;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-muted);
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--text-muted);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
select {
|
||||
width: 100%;
|
||||
padding: 12px 12px 12px 40px; /* Space for icon */
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius);
|
||||
background: var(--input-bg);
|
||||
font-size: 1rem;
|
||||
color: var(--text-main);
|
||||
transition: all 0.2s;
|
||||
box-sizing: border-box;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
select {
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
/* Adjust padding if no icon wrapper used for select */
|
||||
.input-wrapper select {
|
||||
padding-right: 35px;
|
||||
}
|
||||
|
||||
.select-arrow {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
pointer-events: none;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
input:focus, select:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 3px rgba(0,112,243,0.1);
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
cursor: pointer;
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.checkbox-group input {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.checkbox-group label {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border-radius: var(--radius);
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #e2e8f0;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #cbd5e0;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
border-color: var(--text-muted);
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
#result-area {
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#showssid, #showkey {
|
||||
display: none; /* hidden by default */
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
#showssid { font-size: 1.1rem; }
|
||||
#showkey { font-size: 0.9rem; font-weight: 400; color: var(--text-muted); }
|
||||
|
||||
#qrcode {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#qrcode canvas {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* History Dropdown Styles */
|
||||
.history-dropdown {
|
||||
margin-bottom: 20px;
|
||||
text-align: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.history-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 0.85rem;
|
||||
color: var(--primary-color);
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 100%;
|
||||
background: white;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 5px 0 0;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
width: 200px;
|
||||
z-index: 10;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dropdown-menu li {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.dropdown-menu li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.dropdown-menu a {
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
text-decoration: none;
|
||||
color: var(--text-main);
|
||||
font-size: 0.9rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dropdown-menu a:hover {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
/* Hide extra buttons initially */
|
||||
#save, #export, #print {
|
||||
display: none;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
font-size: 0.8rem;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #999;
|
||||
text-decoration: none;
|
||||
}
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: white; padding: 0; }
|
||||
.card { box-shadow: none; padding: 0; }
|
||||
.page-header, #form, #history-drop, footer, .btn { display: none !important; }
|
||||
#result-area { display: block !important; margin: 0; }
|
||||
#qrcode canvas { box-shadow: none; }
|
||||
#showssid, #showkey { display: block !important; color: black; }
|
||||
}
|
||||
126
public/sw.js
Normal file
@@ -0,0 +1,126 @@
|
||||
var CACHE_VERSION = 1;
|
||||
var CURRENT_CACHES = {
|
||||
prefetch: 'prefetch-cache-v' + CACHE_VERSION
|
||||
};
|
||||
|
||||
self.addEventListener('install', function(event) {
|
||||
var now = Date.now();
|
||||
// Here are all the current files QiFi caches while Service Worker installation.
|
||||
// Add files as needed and change CACHE_VERSION at the top of the file.
|
||||
var urlsToPrefetch = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/style.css',
|
||||
'/style-responsive.css',
|
||||
'/bootstrap/dist/css/bootstrap.css',
|
||||
'/qifi.png',
|
||||
'/qifi-small.png',
|
||||
'/manifest.json',
|
||||
'/jquery/jquery-3.3.1.min.js',
|
||||
'/bootstrap/dist/js/bootstrap.min.js',
|
||||
'/jquery-qrcode/jquery.qrcode.min.js',
|
||||
'/jquery.storage.js/jquery.storage.js',
|
||||
'/print.css',
|
||||
'/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2',
|
||||
'/bootstrap/dist/fonts/glyphicons-halflings-regular.eot',
|
||||
'/bootstrap/dist/fonts/glyphicons-halflings-regular.svg',
|
||||
'/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf',
|
||||
'/bootstrap/dist/fonts/glyphicons-halflings-regular.woff',
|
||||
'/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2'
|
||||
];
|
||||
|
||||
// All of these logging statements should be visible via the "Inspect" interface
|
||||
// for the relevant SW accessed via chrome://serviceworker-internals
|
||||
console.log('[Service Worker] Handling install event. Resources to prefetch:', urlsToPrefetch);
|
||||
|
||||
event.waitUntil(
|
||||
caches.open(CURRENT_CACHES.prefetch).then(function(cache) {
|
||||
var cachePromises = urlsToPrefetch.map(function(urlToPrefetch) {
|
||||
// This constructs a new URL object using the service worker's script location as the base
|
||||
// for relative URLs.
|
||||
var url = new URL(urlToPrefetch, location.href);
|
||||
// Append a cache-bust=TIMESTAMP URL parameter to each URL's query string.
|
||||
// This is particularly important when precaching resources that are later used in the
|
||||
// fetch handler as responses directly, without consulting the network (i.e. cache-first).
|
||||
// If we were to get back a response from the HTTP browser cache for this precaching request
|
||||
// then that stale response would be used indefinitely, or at least until the next time
|
||||
// the service worker script changes triggering the install flow.
|
||||
url.search += (url.search ? '&' : '?') + 'cache-bust=' + now;
|
||||
// It's very important to use {mode: 'no-cors'} if there is any chance that
|
||||
// the resources being fetched are served off of a server that doesn't support
|
||||
// CORS (http://en.wikipedia.org/wiki/Cross-origin_resource_sharing).
|
||||
// The drawback of hardcoding {mode: 'no-cors'} is that the response from all
|
||||
// cross-origin hosts will always be opaque
|
||||
// (https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cross-origin-resources)
|
||||
// and it is not possible to determine whether an opaque response represents a success or failure
|
||||
// (https://github.com/whatwg/fetch/issues/14).
|
||||
var request = new Request(url, {mode: 'no-cors'});
|
||||
return fetch(request).then(function(response) {
|
||||
if (response.status >= 400) {
|
||||
throw new Error('request for ' + urlToPrefetch +
|
||||
' failed with status ' + response.statusText);
|
||||
}
|
||||
// Use the original URL without the cache-busting parameter as the key for cache.put().
|
||||
return cache.put(urlToPrefetch, response);
|
||||
}).catch(function(error) {
|
||||
console.error('[Service Worker] Not caching ' + urlToPrefetch + ' due to ' + error);
|
||||
});
|
||||
});
|
||||
return Promise.all(cachePromises).then(function() {
|
||||
console.log('[Service Worker] Pre-fetching complete.');
|
||||
});
|
||||
}).catch(function(error) {
|
||||
console.error('[Service Worker] Pre-fetching failed:', error);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('activate', function(event) {
|
||||
// Delete all caches that aren't named in CURRENT_CACHES.
|
||||
// While there is only one cache in this example, the same logic will handle the case where
|
||||
// there are multiple versioned caches.
|
||||
var expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) {
|
||||
return CURRENT_CACHES[key];
|
||||
});
|
||||
|
||||
event.waitUntil(
|
||||
caches.keys().then(function(cacheNames) {
|
||||
return Promise.all(
|
||||
cacheNames.map(function(cacheName) {
|
||||
if (expectedCacheNames.indexOf(cacheName) === -1) {
|
||||
// If this cache name isn't present in the array of "expected" cache names, then delete it.
|
||||
console.log('[Service Worker] Deleting out of date cache:', cacheName);
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', function(event) {
|
||||
console.log('[Service Worker] Handling fetch event for', event.request.url);
|
||||
event.respondWith(
|
||||
// caches.match() will look for a cache entry in all of the caches available to the service worker.
|
||||
// It's an alternative to first opening a specific named cache and then matching on that.
|
||||
caches.match(event.request).then(function(response) {
|
||||
if (response) {
|
||||
console.log('[Service Worker] Found response in cache:', response);
|
||||
return response;
|
||||
}
|
||||
console.log('[Service Worker] No response found in cache. About to fetch from network...');
|
||||
// event.request will always have the proper mode set ('cors, 'no-cors', etc.) so we don't
|
||||
// have to hardcode 'no-cors' like we do when fetch()ing in the install handler.
|
||||
return fetch(event.request).then(function(response) {
|
||||
console.log('[Service Worker] Response from network is:', response);
|
||||
return response;
|
||||
}).catch(function(error) {
|
||||
// This catch() will handle exceptions thrown from the fetch() operation.
|
||||
// Note that a HTTP error response (e.g. 404) will NOT trigger an exception.
|
||||
// It will return a normal response object that has the appropriate error code set.
|
||||
console.error('[Service Worker] Fetching failed:', error);
|
||||
throw error;
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||