From 3c42ca96687f1b3bb1f8d7c6eb3b22fc1263280d Mon Sep 17 00:00:00 2001 From: Ivan Carlos de Almeida Date: Sat, 13 Dec 2025 02:29:42 -0300 Subject: [PATCH] main beautify --- .gitea/workflows/release_build.yml | 383 +++++ Dockerfile | 8 + LICENSE | 21 + manifest.json | 4 + public/favicon.ico | Bin 0 -> 15086 bytes public/images/icons/icon-128x128.png | Bin 0 -> 2777 bytes public/images/icons/icon-144x144.png | Bin 0 -> 3088 bytes public/images/icons/icon-152x152.png | Bin 0 -> 3211 bytes public/images/icons/icon-192x192.png | Bin 0 -> 3889 bytes public/images/icons/icon-384x384.png | Bin 0 -> 7997 bytes public/images/icons/icon-512x512.png | Bin 0 -> 9561 bytes public/images/icons/icon-72x72.png | Bin 0 -> 1884 bytes public/images/icons/icon-96x96.png | Bin 0 -> 2267 bytes public/index.html | 263 ++++ public/jquery-qrcode/.gitignore | 1 + public/jquery-qrcode/MIT-LICENSE.txt | 20 + public/jquery-qrcode/Makefile | 32 + public/jquery-qrcode/bower.json | 26 + public/jquery-qrcode/examples/basic.html | 29 + public/jquery-qrcode/examples/demo.html | 24 + public/jquery-qrcode/jquery.qrcode.min.js | 28 + public/jquery-qrcode/src/jquery.qrcode.js | 89 ++ public/jquery-qrcode/src/qrcode.js | 1237 +++++++++++++++++ public/jquery.storage.js/.gitattributes | 22 + public/jquery.storage.js/.gitignore | 215 +++ public/jquery.storage.js/jquery.storage.js | 82 ++ .../localstorage.jquery.json | 31 + .../sessionstorage.jquery.json | 31 + public/jquery.storage.js/storage.jquery.json | 31 + public/manifest.json | 56 + public/print.css | 17 + public/qifi-small.png | Bin 0 -> 1103 bytes public/qifi.png | Bin 0 -> 10265 bytes public/qifi.svg | 93 ++ public/robots.txt | 2 + public/style.css | 304 ++++ public/sw.js | 126 ++ 37 files changed, 3175 insertions(+) create mode 100644 .gitea/workflows/release_build.yml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 manifest.json create mode 100644 public/favicon.ico create mode 100644 public/images/icons/icon-128x128.png create mode 100644 public/images/icons/icon-144x144.png create mode 100644 public/images/icons/icon-152x152.png create mode 100644 public/images/icons/icon-192x192.png create mode 100644 public/images/icons/icon-384x384.png create mode 100644 public/images/icons/icon-512x512.png create mode 100644 public/images/icons/icon-72x72.png create mode 100644 public/images/icons/icon-96x96.png create mode 100644 public/index.html create mode 100644 public/jquery-qrcode/.gitignore create mode 100644 public/jquery-qrcode/MIT-LICENSE.txt create mode 100644 public/jquery-qrcode/Makefile create mode 100644 public/jquery-qrcode/bower.json create mode 100644 public/jquery-qrcode/examples/basic.html create mode 100644 public/jquery-qrcode/examples/demo.html create mode 100644 public/jquery-qrcode/jquery.qrcode.min.js create mode 100644 public/jquery-qrcode/src/jquery.qrcode.js create mode 100644 public/jquery-qrcode/src/qrcode.js create mode 100644 public/jquery.storage.js/.gitattributes create mode 100644 public/jquery.storage.js/.gitignore create mode 100644 public/jquery.storage.js/jquery.storage.js create mode 100644 public/jquery.storage.js/localstorage.jquery.json create mode 100644 public/jquery.storage.js/sessionstorage.jquery.json create mode 100644 public/jquery.storage.js/storage.jquery.json create mode 100644 public/manifest.json create mode 100644 public/print.css create mode 100644 public/qifi-small.png create mode 100644 public/qifi.png create mode 100644 public/qifi.svg create mode 100644 public/robots.txt create mode 100644 public/style.css create mode 100644 public/sw.js diff --git a/.gitea/workflows/release_build.yml b/.gitea/workflows/release_build.yml new file mode 100644 index 0000000..6a6ca31 --- /dev/null +++ b/.gitea/workflows/release_build.yml @@ -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<> "$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<> "$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<> "$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<> "$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 }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9d174f1 --- /dev/null +++ b/Dockerfile @@ -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 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6603463 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..eca6cda --- /dev/null +++ b/manifest.json @@ -0,0 +1,4 @@ +{ + "version": "1.0.0", + "author": "Ivan Carlos" +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ae3794588b467ed2e7bddef13a6cc62b5ace7fa5 GIT binary patch literal 15086 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 literal 0 HcmV?d00001 diff --git a/public/images/icons/icon-128x128.png b/public/images/icons/icon-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..855c723b284870c006e3d3c720fcddcc893a6d51 GIT binary patch literal 2777 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 A6#xJL literal 0 HcmV?d00001 diff --git a/public/images/icons/icon-144x144.png b/public/images/icons/icon-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..461a6872d2e96bdfe595dd1dfba07ed1bc01f6c5 GIT binary patch literal 3088 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 K;Fm^T5B>oaQMdd6 literal 0 HcmV?d00001 diff --git a/public/images/icons/icon-152x152.png b/public/images/icons/icon-152x152.png new file mode 100644 index 0000000000000000000000000000000000000000..cf9b173fe6aac6415487bb2f428985a4255b9664 GIT binary patch literal 3211 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 W+5XJ_(pRDs2K2R!;We5LasL4)oFjAq literal 0 HcmV?d00001 diff --git a/public/images/icons/icon-384x384.png b/public/images/icons/icon-384x384.png new file mode 100644 index 0000000000000000000000000000000000000000..fe02b54ead71ca1f5237d80b40b3a554f8123bdb GIT binary patch literal 7997 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 BS|b1e literal 0 HcmV?d00001 diff --git a/public/images/icons/icon-512x512.png b/public/images/icons/icon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e4be80aa4db588a41a6163656d6104917701d8 GIT binary patch literal 9561 zcmeHt`8(A8`}S)lTF6$CXi)A@S}0Lt85Ks!mQc!;%2KF|eVJ*$MME1!)(IiYT_n3w z5s9&uED4zz*~Y%kbG^I2&-45P-{bQf-{W|G;+T2QYrC%NJkRTVy+h4R$U*{A0tg|Y z!-jgt5W>U1c(igk{F(4;oy0CgD&9BTEXF5hUhqC;4U;Nq z9O{#IEZXF6NlRajIx}N;TLqgS+(66p9~oIJrXkI#l9bNiYJm2oqPy2n8ou(r9=tI zrCFi`gjjyCQgK5ZqT#n9WG2Utkiav9bgzmdq!bBD=Z766>i++S{$Cuy^V-{O>dZ~; zOerP~x$Nsa=G85&9H&#F^S3Y#QMTv&_ebWr5d|fVO|Y933BDbdnW~8{5n5bBDGiT~ z-MmE;Sv%Q0+l`5Q%a*S;v`9S~O2_HfYVd-mzBxbPm0AA}Odo8!E|H)I7UNSdx)l!PA`+3*+_VD#OLONu$!hX*ylZ07vst_Z$78=Fjb8NxJ5gP93qf z5B8?4Hr!P*da#|+WXy1S7PhlUiQx0qk*WI556bEP7Etv@+VZ9pqiDB--{uB$64bvs zTyHL?@|x336Y}Obnby=PPK&bla7W>X_ffuG{)%-8B+`!4Z&XUB+Ap|H{r=XHkW)72 zHmT#mX-;q1d!hA3B6SZU-p89cUC7F}l2X5TqWw(Rj-_d-eU}_8@$I!8g6+qr+VV8SS8L=r)!|j2H<(?VnpMz6%*s8u zn0oHTo1b@Fzq=P?Wy6!%Hbx=ve@f_D#ncPgg{c9LkP)Y3{EJb&&iqA6c=AwgrLgOC zr*8!PfC|5yZb)Ny;7DoH()@^h=Y+IzeY|#)NAZxAEoqJ!(P*sE`EY0X0$0uP-3_;F z-1aXsW4%oyA=AYyN&VVbi>*ir5nNaeNv@wCtFmd!gblR=CjO>3DR5a8^^s- zh+CtaNf<@X75NhhLvMQNE$<8Md2?;9#Y3;d=uM8?Z8sP0^xh-O9Pa&i*Xa6kp*wWG ziUc#%ejvUy!E;WdJ7`?-d{eZ7smsDgjWY8ciY32a)@BrnpyuoNhFjbu{n*I2U>UHzX2UCh+$^=@;6vnU~#M^INPkqiCIw zovsj}F#0kxHGYTvStZ;W_B+8{FQVQ-FansDr4Dv=s+j)$IZ&Yaa`!UYnwg>EjXLw+ z>Qm>3i*sVWb*EdTvGxh21WpgL6PDNsjh-Rnv2L|ah!};>4HwoLt$-9iyQd?%$-0dD zXrf}7h`9F2HFDSRxevK~uf6KEHEaXeDXuAxtnSd8#9M8BJ11m$W3ra%_(UfqWVFfi zYV33wZ`ik7nvlZoBk+5r*F4}WHNm-11uE9m6nFHeHJdXG^vSMn{!e~vi9UTMT~1fi z0!8U|RVXBcOgmGjoSEi#E;Srjk)oXa=G~PeG@oZE=+4sOy!Al0$cEj25s#QdRphuy z_bj*}+Uqp5-9Tol-gL>oP?q2sp*{8dwdiYWr2Bn5)?19bnm3qJ-c?6Bp=0!;inMf@ zvYM94KzW-*GzVhK^VyRs=RZH5C>;sjMO?0HVUBF>CF~=IONtS$N9-TUYs!$ng>#V> zs7ToN>#IUg^YzYL(#L8wj1bNR?S(RdrGc?YmRgtMnAC)ne9)vFArgwpTKL5lz0)RzB)0S zkeV9*k3))al>3LNLcd83LE*ZYrjCoUI}d1|u1{~=2f38k|%?PcaZr<9iccdyM#v_}gv|7? zl)~~Z^}JIHocaw|_VyY-Q!MfRfbArJ5Yb@F`L6gWr2hTlD+>3MBDu<8fB)KL0CMl# z+CGZt%*YWSJB!NG1lejhFe!8-3g>X3ipU>R2ix{kYyel28 zaP)EHue#WRW-682_X%+CG#~e$;@RA|$u|4)EP3C@BS8b2Yz(`aQjNptc(XwLIvnfU zE9Xj`O=Y~Lc{V3A#{>W%!_M)vfBz$+;BothT2~@T(ORm`7)xM?7rWr4*?vSh>*3Or zCr=$x?T}Ii+UVVEE>ze#;@xIz-8nN}=U?-1>(G>!j@@pq;it@;vUU$rX6eSu@5NgD zacH}pY7XMYV4$u;{CLcNyiTKuNtTV0H)b40i z$&Uz)4tY{Fi2_2Ksn*<*$M=JVy%NIcxh`WIYa|BSrw?rvU4Hb`+B~^~?DxEmi zez}U!WY3$lPuo+k^~%voWMk0i+m@*>ug=%qp&wjUt$fXZzHtL#8S%6jEXb<7YNOXt z7{q6Os30vTs9!nXZ+(KjU=VM<+Y~Co#ItnLit=>J{Q8p@<1Txdx7WVNqj?5>MrE}vbObu_P1RW=4Bl;bZUF?#*ogv%3!N?TobBC) zipDYG1gG?VhA$|n>WLcV96j>M7pKwqHc_OLo-SAsGC#DQ;P?-B|Cn<^un#5t9EN9> znj*sJGtt_vzvnJQxAz~=e2w1-0QqlDdf5^GuGl_ zWZQ2}3!)u+cvquqUmSI5){(@MPfG|1C>sBw$#BvQnC*|oXjPUVkEuHL1^am$O1O&g zDa2iq`z1~j8#(BZ%ljl=YoEmow~ODuxS!Y#3}Z1p+}Q+rfxsOfzwn$GesEzTjdYie z9%=RL-=$2C@;$ZVWK$V_X0EMVhTwRQt3cw7itt1UjJiI5L|W((VK{9+Hqupk;qtQO z&mW|2NB;FZ_t)PPh#xoi?-_Ld{8$W%I?n6MeoWmkRV7^8Y<*#PGiBqGi2GK?o3NCy z$JsbGA?*@pMd zgQHB#dfmPaip*Wj7FT_t%(vY&jU|~?vu7#Rf3ua266VUR9@#@2{4_i(!Jfobd1$FHp%8%n?g>)tIPX$c6XL`(+itFz-;*b!_me^{4K zkn+5+R^jOLU(1@!<9u)Pu?&qZ5qJB@X*`Doxp0@>;K-#1mM=}_Fr4x@g7z~9VI?*$ z8F;g{o=)d@e+BKa_iLG|#N+X!C&;s!XGkD66AzvCs@uyHBX&SCNWPw4`iY5oAxRZE zIEy!_+mQb%2LjNwH#~A$XXFRuau}VNQ6!;Hqc?++xVyL2P}<{-BPCpdyEIdGh11PVk3l@dt?c*8D%jd49F{|zdA+elU*K_B3dInAPM>ye$q1etL@(Tx|l~$-;Y?5J1*ui z4X`1+Lxme8ICHffD=TFCH#g_lmjfu6wT-MMjLZ%sn2L}VE>XgLTJkTnskt;HvLu_2 zT8Pt%ga@Q>GmO(W{uFNSS`N`ouS#={#ekD52HBDpZ5ObWxO{Jucn-qk5^MnKgKFsY z>vJgGT+n;_LFh2DchZh+MMGzS1DIlNy@dnuVf2QKh2wl?8L3Lh-zmE23DMh)a-;qE zpmP%V2o|XQu&rl+m|f7=2b=iYW>q6Xx@6ZGd;@@P9`LF`mCL5?i5Uin_-C$7srpqe zjzjrvbO>T`n zvdavlokb_-&Ajci4SUY(kzJCqy9qRUBFcL}jK41Xr-a39o)7x2NR(F#!kuu>Z%LNV z8~qjQDM1zZk&w}wYG{W;TQ0yjRh=E2nLOz%dcxrxyV7KNbOT%TE9ZJRmFBVY>$B+c zNI5I-7zA8J$Q}~HsD!Rj?Bcy$-+OxmBBb;P>Jq4-^>LaS#Dhc4myez8(sR4HPHKtA zkD?A&@}@GKaMhaKV~4#LjuO2&$7{v@qR|^h`4dkSctGks@2%TuV{5IC^(t7rVX-V| zl_)nPI03K1j<+2YvfOW&g|~SKpT%JKaqs!P-iuy8>axa~tH~Sb0O%Rk2x){ZU0eLsD?? z-h?N}#q-bp&ZeWh<85%jg{#m|7R2V=O+iQ;v$j~PSFF@^f>b!ehm9gn`#&+uL7=trSu2qvOk4sDE$keVNbb(17G2JT29i;Rz~JZq!RwpO{L|Vq-!-ROF#gTN$d<9gUzBM)i>frd_}_2uU|h;)pHWPa5wcHyy$X=JTFyrlr zomL+*^@^!#y(b5u+b3-+;b*WNa6{ z70QcyE27ILQs88@mS%d{*F|*xrsG620REjKv?U;cf%t4GH&g&D()%w&7KK}G?6t(b0YMHw%y^Gi0rmy*sNqL61!d$-z)vQ{A!{9h zai}GKqIpsi-@wFZdB@ekH*byNSCxPFDAuoHsmv<%B^|mA0NOKD&}}l^;bjWZ#<(}K z6NDRNc0QO1nb=Id< zgU4gL!+~$9ylH9#C>?1K3}AA+W~~gPfI(wGcJaj|B&KXB;Z{I&#+Y7Zz<`jRO--gG z0)b(C8HTesxtZ}q|qS?;qmueD4p)oBpMWN0BdN6 zDX&&*aaquPe8XF;H*5y}Vo47D4BzwRZ+fm_n_Zwg6t#_O4gL~PYTC(H=YcLP?KVf- z^Q*-M{d)Vim^>zSz)8uE+bAHKvQ@AG&OF+qWb7j*$;h}O??DMn&H%ft zJ?R_=3!{8kXZ`F?!@68|NX;Er9Wq;MZ$LirA}3@rlO1_TI20N|pKs@)Dd08eV?y}% z$cOdz8$qr`#T22w-m9`$t1KC|*XQ?FHL(6zl1A>8LK;V-ZGBEs!avkp;3a?o3Y~U- zb2Go(Jlm8i1*$$^dN@wOds|P)6Kl>avdE-65c^Zc_6W=Y z`)3+me)7bRXWVn&E^jf|dpB22Mbn5xwbCLEkk_P1aPxC>rnycFNEG`z97^G)S8+;4bzUq5S^Nj5e6pSN2m z0dSABBEetkhFG4#tjZV_$Afzx1cQ6T8BOR0bA$rw+;WAoJ18*1(s*=>NXY1Q&ZsPs z$a##9jH5Ij7RpyX-L+-(d-swLIAm#!z92oD0l0m5PyC~JZJ?1SW_lyiEKIfYV`kcv zAknuMCJX4w)O;~*xqBXpGt1+m|ISL^vhg^~YAu+puRgU__EH6o4gGVL!aOD)-Iw;X z$EHzfTda@cs+WdGma;cl@#X#6lQ4VK`$4R_yG?#=XVPt%n7PTpg3r7XtwPTU7PIAY z;hyuKpZiQntGSp-D-7?O-Q)VqUj{LY^j5H3W>-xohwVJ#^f5e?{_->MevA+7lv9Ic zJP2bE%kMHIz?PQPGi~p?|^KAUFH69xx5sT`@lsRQC%L`HK^kf9qTU_)jhL z8o=xn$q(yjzoE^@ZtZAuP4DDZGZ5w=c|TH;osRVl;&b@4ZdyPN__!@&m4{kc$@gy6 zs*O!%4>WO{#A#~IA!LrtR6ViWpT&ci=JoHY*uAX?ogH}UMc>r7{Nc3z8|YIp1M9_4 zL0vnAXy3BhwaZ*rn`({e5&IunK*y$*2j8fBbDAftsgu?)7Jhu7-V{7Z&j285%*g`p ziyAIV(pj9TBGKt@&-A5)_)k}s^oXHaGKc}79`(hkg7Ulcx0@GwNlQ*}H*p&C^Ab_e zXD~2M)vfS@`v?YORMDfbKZ>()1!}*AA64X@tpLOEq@9(#sm|=#Ik01yxwrS>n(yHo z?r7BpK{cm;iB`0VPkh2V_4&yjMWO)a1WNQk+nhtM2?l;jW{3}h3OHiF#Eote+*Kk9 zeiB;=gT79AYR1VPJ0=*!=j&VXt;X*%hM-59ha-z zGS8#-+xT2QK#0dakeq+69F&azv=>0CarsMV=qT>Q!CCK$(VogC*`?9jEG6SXNL6F| zg`ex2z?4~w&At_!R??%89zlD<2a2Y2dLxCq{OIf_lIg;-#6|d$-5+gl0{we%5lFgZ zhT7c{elI(YhVO6(O9_22Ls191WaT`MdI9uV-fqgh2rBF@$0311CvMwuB3F(mtZOb# z>%Ro(Xj$3n-zyho64G4`>LN`!r(`#r&^`EeV!oTF;|@p72H|p4aQ)73x<%luUb*1T zH*!MK>f5y!Cz_a-(H+n;S4&R)_@tO{+F10dn(Hyj)AkyT1ug1NU;|1#NmqqGQK-KK zmjDW-352BOkYl}U|Gj${$}E=irg@EVzFiv#D`?M9UZK|e;K>&2w71t+q>TiR1h%lR zrF^II7MsB4bJ5VJNtr`qWViLw96K!ydQTF;>$9rs+yt9>?_HnQLa{H1hh?+~cV?}Bq|;`+$aDwN*pVRgdsCWW06omw9} z?Db)2CfYi*x#vW$mXzg+#rf$RgZG>>f2?$~r`fi}tv@w$;o^ZzUfIH~9K6mF4f^`N zh>|WWU32Q^FqjZ6;j;_ndt*=Ig>`0tj<%-(OFr9bk*ZfkOU77s2#&0FVLEn1xG9l) ztWb0K4WsB~ymp|Kk$~6M>Kq}=CS7bC0@k?H*#BVQKnEB}Y-k2U!PZpcPfP_LG}@zl zaf&l%7$KwG2#P6cx$wrvV6T;OYARjeCg;2jGQTG}w}3 z!fMO0$IvPbnSIh6zY91=G_Y~HyEkJN#Sy*l(D4n;;%KQ2u9Tkq>q@jRtN$oW_)RJr zE$8GJImC;;`J@mF?x>5607|$7L~ETksb5>Q$sRM6NZ&*g&9@>@@T-C#|KUCojIfOM zzgA+GBn|HtX2+XAlUm1ZlN@?zVRE?gmJk?M4HR)?vllKkDd1`9RTTsmxrVPuD`F*wHQrdYHreS}S@3-8nb6I;d(6;@&9jN| z#NTW7xivqhRByVCZ4zEkFglA};KgO&NF>%UVz(Q{IEPS7C9|DL^B~9E zqAP+N%m%*1F<{Z#`DrQlBZNYJJ#mIv+zUF@<1W2h1<4{6g|9gFM8nh09Y&jnE-eo* zjSN#6!c^{XNy>&xmpgRxp*4P3&0m+_=+oq zKduQjz7Jgsk;1OGG$ru4XT?#}sjerU7-Xq7vY3n}+=`8&aNL_Hp~AYowZ(kc{S6zM zqPz6_7z+H6mzXjjS2Cg?fBFy8Bl7Kl>I3h#n+WU%RXP=`9gi0NFe0a7*1k zH;=uu;1Ub{DPt7gB>8WA!KAsraxkAII;6DzsU-t@xuuIY8nYH&Zt=b&-0)?N>Q^dG zz|(GV7jC!F;IX>@@Og;;XOI7X{&aIZ96~&v%zfEM9YXrM;gui1`=G6kjujF&Km>kH+*LL*quGk;t=@XYpaiNRdJaDJ3JxD3YoEy_Z^WcDOA&vl z)Jy5=MF2<8Uyq0&1`$CFB7zu11TlyRVh|C;AR>rCL=c0BAO;*kE%du2|5Jgv{qVXV zPfCf$&Bao30v=SDfcck7X>hv$lmE}d09IH&{kbGz5j2vAm*jYT8R7R53mtNEb7}JA z$<(P+r>s2x^XJcW@#4kA=WO?6Y*J8AAf7*eF0zID_wS1~ZQ7`@gf`9p_~DscGWP7y z5)>9h1TlyRVvuqc%g@gz8P}+}x!Ko!Lx&EfS+i!DbjOb$rw0!nsQ<%ekT`bi7|GyE zIXOAXcKi12Y1y)6)U~T;&)z>lFgSJURF#GY4H`sUx^$smzkbojj~~_K&Ye3)7cN|& z#>PfZJHv+$r;Qsonsjw_bqRtn5!kJ%si9AwK535+lkeTTH_e$d$D}`U((tH3G}ozfBt;ieR5KXH2?JJQz5g7C@L!QT_y}0 zHq2(bva-^2P3O*?#fuj&9LIm>&K>`0e(Tn)bolULDlIK#uf^&(b?Ou?UAk1)yd$uR z;0k`>bdS|#@ma@>wFp|hdNr+Izg}~Azk%4afB$}4oQXsJLRz|@V2zRAzkk;vh>byV zxzd&`TO7ap`t>V4d-hEEb4f{wBE@7}>i6y2N7t@hqc?Bfn7+Gu^(w7ewJI@4td^TM zZ?Xf{fm8fjym&E<8#hjEWAIFjwR`vOtSy$H4I4I4$BrFs`hy1#()R7!6+!sr2M-=h zJ9g|)!P|&_{rb_$l`E;by4s|F{P?kwWB}Lg+qbXl3cz<(Oqnu8NiuK^c4cK{LM}yH z5R@UOXzkjywwq--7ybM9cfDug#EB~VSTTM2bklY7 z=FL+BG)hH9g%&~56@3$g3@lwoEl58c|h(hEKl3Er++;K|j#y*J?e*XMPSFT*K>4yv%!VX3Um=p@g z_`go#G;t&WBk*Hbbxv>%(unXqdh{^q@bE{E9tEo5N9%hAY+-UCHSY+M}=m24p-E%moDgxQ5UW*hSD4X*SLyM z86%ExaM}ySvquoXYIIvAAScKziz`9cMt~$)lO%Nbzbxihqt@UKG&D45xu*`WX?Dws zAP5N|D7X@Y{tg1Fksz#~qM{3jC#p^1-XzA!ZH@k7J`J#^*e zb?da={z?Z> z6vwOe=uZ(C=(yNlO(0fKxySeaNU5Qax?%vpF>w`n{5cc{98WY+8cKnX$iGqwya?c2 z3}?lyw+!Zixp{8sL!0Kqf`}jn5kU+hf*3>uF^C9a5D~;6B8Wjm5QB&y2LA^D0RR6Y Wt6lP+0FL(n0000`r+IKBpr!A+sVYzxBt#(-V<+sIbTF7Ejv93(9-97o(;iOQ|x`CjW!x;?P* z`w8k(pTq+~faOA5)rsU-E)*yTYyf2li3hu20UzanuC4#tfLVwQ&-v7|$J()gE_?}z zp|8fSQ@UK>P&62E_S6nsn}M=>Ob<@6QgqC@+?7o&47Rh#H4T9%-#-@WE0OWrql*O` z?%$yUVSYQy1{J6&7sdxH;AuPl^VXL4#2{TlRu*TNbfUhQ zs&v^Ib_)FK|EIe;|1D!_u%JKV?%Q08JWZc%&7hNPr(Yk)V-+YZQvW8UhdTF9yyvdN z%vljtJoIs)OnMRu=AFBL$UQd++RBgfOe@0*AY6Ux`Fw_69k`b*VYP>?7zY3lp1G z?VH_40XU_0=?1~-3XoscG zdQ6s@f7sUg9k;(AXX-Rjx(VtutLTjVqy5sse0YJ~cC081Q-1y&n`$)9Qrp98I!@9k zS1($0pgt(bvLdufqm}Zr?u+G8 z@WxV~r!Fp5J0B7YawqH)-KUJU&EE`LP1Ofc7B**^**k1+%uZ4V^5TnbjwIqYKt+_* zxvC=Lz(9?gs5gG)tCO{~gF-JheapLOanqI2@0D(gT<^F=;><@yPmlrAA^k*;iK>=~ zijxtJ+n!?_?6@{yGo#TRHz&63P3cY#b8{W=S(`KfyX*$4dd`NzL_BaA21=Xo=Ugwx zifyaB4f5_(P#ok=ap@A(jbCfLBaG*oi9?^1BQv&R`R|-x4Uqy6Ou(yC^*y>kR4R`! zL|rkK3nw$|)rQ3|x$2wmNU{;Z22M)>Ackf6Sz>UT&X@Sc@^`W;Y{EA?vq~OXU1_Vz zTW4@P;nuBL?X4ovB?u5X8R~zu7poC)@Y7i9&znlgCg4L;`BfzKRgGxFOK{g)L6&^9 z(qy9~RQfEBGcFJIRgu)3Bj51w;U5R75TqZ#^0mjUr1x%mj@Cmo1JPCShS$ zf}hbougjq}8~Cgfpm01V(IPJn7RpPDXk(=c6k%$*k!1Qyjo(a+y;Xn*EcX0OuVz9B z*eqSJyn;dsNG&olGW1NDU4t}j$T$3w{x_CrQFZ>D#wpUh&4sQo^sS@AyF7;(ddX08 zApM@EqxagR4U@o0pFgc!UurUg@ql5zNI#cP8UfiuEX&tUb6;#T);bZmVT*6SW~k2r z2x9#C68t+Dp#TbNl(8uTwEBk0ah|k3Qo)*T(lz&9{VZvvtCZM`^ekR3GI;Hu5Yy zBYj#JCW?shoJqT2)`3V9!w3)0iP@1CBUKISGQ23=WTxI~pPJVtXQ8!_OLOz{a?%Ca znafzFS%g-`EBlk0dK5DYY!Qn=?8o4dAC!SXu_L@GeIT^7sr!%7j9Uxl9ho2Q{IuWI@ zwOw-g(MDbjN7biB0}2ams;$bYZX+1Po1NvE)7M6#Dia8V)hCs6piaZ5X(5(%oYOV( z^72Fs2I!%Kr2PV278XkSD|t`uXi + + + + + + + + Wifi no QR + + + + + + + + + + + +
+
+ + + + + +
+
+ +
+ + + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + + + +
+
+ +
+ + +
+ +
+ + + Download PNG + Print +
+
+ +
+

SSID: none

+

Passphrase: none

+
+
+ + +
+
+ + + + + + + + + + diff --git a/public/jquery-qrcode/.gitignore b/public/jquery-qrcode/.gitignore new file mode 100644 index 0000000..8d4ae25 --- /dev/null +++ b/public/jquery-qrcode/.gitignore @@ -0,0 +1 @@ +bower_components diff --git a/public/jquery-qrcode/MIT-LICENSE.txt b/public/jquery-qrcode/MIT-LICENSE.txt new file mode 100644 index 0000000..6d32551 --- /dev/null +++ b/public/jquery-qrcode/MIT-LICENSE.txt @@ -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. \ No newline at end of file diff --git a/public/jquery-qrcode/Makefile b/public/jquery-qrcode/Makefile new file mode 100644 index 0000000..9f7320f --- /dev/null +++ b/public/jquery-qrcode/Makefile @@ -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~ \ No newline at end of file diff --git a/public/jquery-qrcode/bower.json b/public/jquery-qrcode/bower.json new file mode 100644 index 0000000..d62f5ff --- /dev/null +++ b/public/jquery-qrcode/bower.json @@ -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" + } +} diff --git a/public/jquery-qrcode/examples/basic.html b/public/jquery-qrcode/examples/basic.html new file mode 100644 index 0000000..5213bc3 --- /dev/null +++ b/public/jquery-qrcode/examples/basic.html @@ -0,0 +1,29 @@ + + + +basic example + + + + + + + +

Render in table

+
+

Render in canvas

+
+ + + + diff --git a/public/jquery-qrcode/examples/demo.html b/public/jquery-qrcode/examples/demo.html new file mode 100644 index 0000000..66bfc13 --- /dev/null +++ b/public/jquery-qrcode/examples/demo.html @@ -0,0 +1,24 @@ + + + +Demo page + + + +

+ TODO make a nice looking pure client qrcode generator + even allow download of the image +

+ +
+ + + + + + + diff --git a/public/jquery-qrcode/jquery.qrcode.min.js b/public/jquery-qrcode/jquery.qrcode.min.js new file mode 100644 index 0000000..fe9680e --- /dev/null +++ b/public/jquery-qrcode/jquery.qrcode.min.js @@ -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;da||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=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=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;0g;g++)if(null==this.modules[b][i-g]){var n=!1;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;e8*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>>=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;dc)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=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++;5a)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;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()-a.getLength())return this;for(var c=l.glog(this.get(0))-l.glog(a.get(0)),d=Array(this.getLength()),b=0;b>>7-a%8&1)},put:function(a,c){for(var d=0;d>>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").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").css("height",b+"px").appendTo(c);for(i=0;i").css("width", +d+"px").css("background-color",a.isDark(e,i)?h.foreground:h.background).appendTo(f)}}a=c;jQuery(a).appendTo(this)})}})(jQuery); diff --git a/public/jquery-qrcode/src/jquery.qrcode.js b/public/jquery-qrcode/src/jquery.qrcode.js new file mode 100644 index 0000000..d329934 --- /dev/null +++ b/public/jquery-qrcode/src/jquery.qrcode.js @@ -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 = $('
') + .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 = $('').css('height', tileH+"px").appendTo($table); + + for(var col = 0; col < qrcode.getModuleCount(); col++ ){ + $('') + .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 ); diff --git a/public/jquery-qrcode/src/qrcode.js b/public/jquery-qrcode/src/qrcode.js new file mode 100644 index 0000000..5cbe0f6 --- /dev/null +++ b/public/jquery-qrcode/src/qrcode.js @@ -0,0 +1,1237 @@ +//--------------------------------------------------------------------- +// QRCode for JavaScript +// +// Copyright (c) 2009 Kazuhiko Arase +// +// URL: http://www.d-project.com/ +// +// Licensed under the MIT license: +// http://www.opensource.org/licenses/mit-license.php +// +// The word "QR Code" is registered trademark of +// DENSO WAVE INCORPORATED +// http://www.denso-wave.com/qrcode/faqpatent-e.html +// +//--------------------------------------------------------------------- + +//--------------------------------------------------------------------- +// QR8bitByte +//--------------------------------------------------------------------- + +function QR8bitByte(data) { + this.mode = QRMode.MODE_8BIT_BYTE; + this.data = data; +} + +QR8bitByte.prototype = { + + getLength : function(buffer) { + return this.data.length; + }, + + write : function(buffer) { + for (var i = 0; i < this.data.length; i++) { + // not JIS ... + buffer.put(this.data.charCodeAt(i), 8); + } + } +}; + +//--------------------------------------------------------------------- +// QRCode +//--------------------------------------------------------------------- + +function QRCode(typeNumber, errorCorrectLevel) { + this.typeNumber = typeNumber; + this.errorCorrectLevel = errorCorrectLevel; + this.modules = null; + this.moduleCount = 0; + this.dataCache = null; + this.dataList = new Array(); +} + +QRCode.prototype = { + + addData : function(data) { + var newData = new QR8bitByte(data); + this.dataList.push(newData); + this.dataCache = null; + }, + + isDark : function(row, col) { + if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) { + throw new Error(row + "," + col); + } + return this.modules[row][col]; + }, + + getModuleCount : function() { + return this.moduleCount; + }, + + make : function() { + // Calculate automatically typeNumber if provided is < 1 + if (this.typeNumber < 1 ){ + var typeNumber = 1; + for (typeNumber = 1; typeNumber < 40; typeNumber++) { + var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel); + + var buffer = new QRBitBuffer(); + var totalDataCount = 0; + for (var i = 0; i < rsBlocks.length; i++) { + totalDataCount += rsBlocks[i].dataCount; + } + + for (var i = 0; i < this.dataList.length; i++) { + var data = this.dataList[i]; + buffer.put(data.mode, 4); + buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber) ); + data.write(buffer); + } + if (buffer.getLengthInBits() <= totalDataCount * 8) + break; + } + this.typeNumber = typeNumber; + } + this.makeImpl(false, this.getBestMaskPattern() ); + }, + + makeImpl : function(test, maskPattern) { + + this.moduleCount = this.typeNumber * 4 + 17; + this.modules = new Array(this.moduleCount); + + for (var row = 0; row < this.moduleCount; row++) { + + this.modules[row] = new Array(this.moduleCount); + + for (var col = 0; col < this.moduleCount; col++) { + this.modules[row][col] = null;//(col + row) % 3; + } + } + + this.setupPositionProbePattern(0, 0); + this.setupPositionProbePattern(this.moduleCount - 7, 0); + this.setupPositionProbePattern(0, this.moduleCount - 7); + this.setupPositionAdjustPattern(); + this.setupTimingPattern(); + this.setupTypeInfo(test, maskPattern); + + if (this.typeNumber >= 7) { + this.setupTypeNumber(test); + } + + if (this.dataCache == null) { + this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList); + } + + this.mapData(this.dataCache, maskPattern); + }, + + setupPositionProbePattern : function(row, col) { + + for (var r = -1; r <= 7; r++) { + + if (row + r <= -1 || this.moduleCount <= row + r) continue; + + for (var c = -1; c <= 7; c++) { + + if (col + c <= -1 || this.moduleCount <= col + c) continue; + + if ( (0 <= r && r <= 6 && (c == 0 || c == 6) ) + || (0 <= c && c <= 6 && (r == 0 || r == 6) ) + || (2 <= r && r <= 4 && 2 <= c && c <= 4) ) { + this.modules[row + r][col + c] = true; + } else { + this.modules[row + r][col + c] = false; + } + } + } + }, + + getBestMaskPattern : function() { + + var minLostPoint = 0; + var pattern = 0; + + for (var i = 0; i < 8; i++) { + + this.makeImpl(true, i); + + var lostPoint = QRUtil.getLostPoint(this); + + if (i == 0 || minLostPoint > lostPoint) { + minLostPoint = lostPoint; + pattern = i; + } + } + + return pattern; + }, + + createMovieClip : function(target_mc, instance_name, depth) { + + var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth); + var cs = 1; + + this.make(); + + for (var row = 0; row < this.modules.length; row++) { + + var y = row * cs; + + for (var col = 0; col < this.modules[row].length; col++) { + + var x = col * cs; + var dark = this.modules[row][col]; + + if (dark) { + qr_mc.beginFill(0, 100); + qr_mc.moveTo(x, y); + qr_mc.lineTo(x + cs, y); + qr_mc.lineTo(x + cs, y + cs); + qr_mc.lineTo(x, y + cs); + qr_mc.endFill(); + } + } + } + + return qr_mc; + }, + + setupTimingPattern : function() { + + for (var r = 8; r < this.moduleCount - 8; r++) { + if (this.modules[r][6] != null) { + continue; + } + this.modules[r][6] = (r % 2 == 0); + } + + for (var c = 8; c < this.moduleCount - 8; c++) { + if (this.modules[6][c] != null) { + continue; + } + this.modules[6][c] = (c % 2 == 0); + } + }, + + setupPositionAdjustPattern : function() { + + var pos = QRUtil.getPatternPosition(this.typeNumber); + + for (var i = 0; i < pos.length; i++) { + + for (var j = 0; j < pos.length; j++) { + + var row = pos[i]; + var col = pos[j]; + + if (this.modules[row][col] != null) { + continue; + } + + for (var r = -2; r <= 2; r++) { + + for (var c = -2; c <= 2; c++) { + + if (r == -2 || r == 2 || c == -2 || c == 2 + || (r == 0 && c == 0) ) { + this.modules[row + r][col + c] = true; + } else { + this.modules[row + r][col + c] = false; + } + } + } + } + } + }, + + setupTypeNumber : function(test) { + + var bits = QRUtil.getBCHTypeNumber(this.typeNumber); + + for (var i = 0; i < 18; i++) { + var mod = (!test && ( (bits >> i) & 1) == 1); + this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod; + } + + for (var i = 0; i < 18; i++) { + var mod = (!test && ( (bits >> i) & 1) == 1); + this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod; + } + }, + + setupTypeInfo : function(test, maskPattern) { + + var data = (this.errorCorrectLevel << 3) | maskPattern; + var bits = QRUtil.getBCHTypeInfo(data); + + // vertical + for (var i = 0; i < 15; i++) { + + var mod = (!test && ( (bits >> i) & 1) == 1); + + if (i < 6) { + this.modules[i][8] = mod; + } else if (i < 8) { + this.modules[i + 1][8] = mod; + } else { + this.modules[this.moduleCount - 15 + i][8] = mod; + } + } + + // horizontal + for (var i = 0; i < 15; i++) { + + var mod = (!test && ( (bits >> i) & 1) == 1); + + if (i < 8) { + this.modules[8][this.moduleCount - i - 1] = mod; + } else if (i < 9) { + this.modules[8][15 - i - 1 + 1] = mod; + } else { + this.modules[8][15 - i - 1] = mod; + } + } + + // fixed module + this.modules[this.moduleCount - 8][8] = (!test); + + }, + + mapData : function(data, maskPattern) { + + var inc = -1; + var row = this.moduleCount - 1; + var bitIndex = 7; + var byteIndex = 0; + + for (var col = this.moduleCount - 1; col > 0; col -= 2) { + + if (col == 6) col--; + + while (true) { + + for (var c = 0; c < 2; c++) { + + if (this.modules[row][col - c] == null) { + + var dark = false; + + if (byteIndex < data.length) { + dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1); + } + + var mask = QRUtil.getMask(maskPattern, row, col - c); + + if (mask) { + dark = !dark; + } + + this.modules[row][col - c] = dark; + bitIndex--; + + if (bitIndex == -1) { + byteIndex++; + bitIndex = 7; + } + } + } + + row += inc; + + if (row < 0 || this.moduleCount <= row) { + row -= inc; + inc = -inc; + break; + } + } + } + + } + +}; + +QRCode.PAD0 = 0xEC; +QRCode.PAD1 = 0x11; + +QRCode.createData = function(typeNumber, errorCorrectLevel, dataList) { + + var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel); + + var buffer = new QRBitBuffer(); + + for (var i = 0; i < dataList.length; i++) { + var data = dataList[i]; + buffer.put(data.mode, 4); + buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber) ); + data.write(buffer); + } + + // calc num max data. + var totalDataCount = 0; + for (var i = 0; i < rsBlocks.length; i++) { + totalDataCount += rsBlocks[i].dataCount; + } + + if (buffer.getLengthInBits() > totalDataCount * 8) { + throw new Error("code length overflow. (" + + buffer.getLengthInBits() + + ">" + + totalDataCount * 8 + + ")"); + } + + // end code + if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { + buffer.put(0, 4); + } + + // padding + while (buffer.getLengthInBits() % 8 != 0) { + buffer.putBit(false); + } + + // padding + while (true) { + + if (buffer.getLengthInBits() >= totalDataCount * 8) { + break; + } + buffer.put(QRCode.PAD0, 8); + + if (buffer.getLengthInBits() >= totalDataCount * 8) { + break; + } + buffer.put(QRCode.PAD1, 8); + } + + return QRCode.createBytes(buffer, rsBlocks); +} + +QRCode.createBytes = function(buffer, rsBlocks) { + + var offset = 0; + + var maxDcCount = 0; + var maxEcCount = 0; + + var dcdata = new Array(rsBlocks.length); + var ecdata = new Array(rsBlocks.length); + + for (var r = 0; r < rsBlocks.length; r++) { + + var dcCount = rsBlocks[r].dataCount; + var ecCount = rsBlocks[r].totalCount - dcCount; + + maxDcCount = Math.max(maxDcCount, dcCount); + maxEcCount = Math.max(maxEcCount, ecCount); + + dcdata[r] = new Array(dcCount); + + for (var i = 0; i < dcdata[r].length; i++) { + dcdata[r][i] = 0xff & buffer.buffer[i + offset]; + } + offset += dcCount; + + var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); + var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1); + + var modPoly = rawPoly.mod(rsPoly); + ecdata[r] = new Array(rsPoly.getLength() - 1); + for (var i = 0; i < ecdata[r].length; i++) { + var modIndex = i + modPoly.getLength() - ecdata[r].length; + ecdata[r][i] = (modIndex >= 0)? modPoly.get(modIndex) : 0; + } + + } + + var totalCodeCount = 0; + for (var i = 0; i < rsBlocks.length; i++) { + totalCodeCount += rsBlocks[i].totalCount; + } + + var data = new Array(totalCodeCount); + var index = 0; + + for (var i = 0; i < maxDcCount; i++) { + for (var r = 0; r < rsBlocks.length; r++) { + if (i < dcdata[r].length) { + data[index++] = dcdata[r][i]; + } + } + } + + for (var i = 0; i < maxEcCount; i++) { + for (var r = 0; r < rsBlocks.length; r++) { + if (i < ecdata[r].length) { + data[index++] = ecdata[r][i]; + } + } + } + + return data; + +} + +//--------------------------------------------------------------------- +// QRMode +//--------------------------------------------------------------------- + +var QRMode = { + MODE_NUMBER : 1 << 0, + MODE_ALPHA_NUM : 1 << 1, + MODE_8BIT_BYTE : 1 << 2, + MODE_KANJI : 1 << 3 +}; + +//--------------------------------------------------------------------- +// QRErrorCorrectLevel +//--------------------------------------------------------------------- + +var QRErrorCorrectLevel = { + L : 1, + M : 0, + Q : 3, + H : 2 +}; + +//--------------------------------------------------------------------- +// QRMaskPattern +//--------------------------------------------------------------------- + +var QRMaskPattern = { + PATTERN000 : 0, + PATTERN001 : 1, + PATTERN010 : 2, + PATTERN011 : 3, + PATTERN100 : 4, + PATTERN101 : 5, + PATTERN110 : 6, + PATTERN111 : 7 +}; + +//--------------------------------------------------------------------- +// QRUtil +//--------------------------------------------------------------------- + +var QRUtil = { + + 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 : (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0), + G18 : (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0), + G15_MASK : (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1), + + getBCHTypeInfo : function(data) { + var d = data << 10; + while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) { + d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) ) ); + } + return ( (data << 10) | d) ^ QRUtil.G15_MASK; + }, + + getBCHTypeNumber : function(data) { + var d = data << 12; + while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) { + d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) ) ); + } + return (data << 12) | d; + }, + + getBCHDigit : function(data) { + + var digit = 0; + + while (data != 0) { + digit++; + data >>>= 1; + } + + return digit; + }, + + getPatternPosition : function(typeNumber) { + return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]; + }, + + getMask : function(maskPattern, i, j) { + + switch (maskPattern) { + + case QRMaskPattern.PATTERN000 : return (i + j) % 2 == 0; + case QRMaskPattern.PATTERN001 : return i % 2 == 0; + case QRMaskPattern.PATTERN010 : return j % 3 == 0; + case QRMaskPattern.PATTERN011 : return (i + j) % 3 == 0; + case QRMaskPattern.PATTERN100 : return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; + case QRMaskPattern.PATTERN101 : return (i * j) % 2 + (i * j) % 3 == 0; + case QRMaskPattern.PATTERN110 : return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; + case QRMaskPattern.PATTERN111 : return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; + + default : + throw new Error("bad maskPattern:" + maskPattern); + } + }, + + getErrorCorrectPolynomial : function(errorCorrectLength) { + + var a = new QRPolynomial([1], 0); + + for (var i = 0; i < errorCorrectLength; i++) { + a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0) ); + } + + return a; + }, + + getLengthInBits : function(mode, type) { + + if (1 <= type && type < 10) { + + // 1 - 9 + + switch(mode) { + case QRMode.MODE_NUMBER : return 10; + case QRMode.MODE_ALPHA_NUM : return 9; + case QRMode.MODE_8BIT_BYTE : return 8; + case QRMode.MODE_KANJI : return 8; + default : + throw new Error("mode:" + mode); + } + + } else if (type < 27) { + + // 10 - 26 + + switch(mode) { + case QRMode.MODE_NUMBER : return 12; + case QRMode.MODE_ALPHA_NUM : return 11; + case QRMode.MODE_8BIT_BYTE : return 16; + case QRMode.MODE_KANJI : return 10; + default : + throw new Error("mode:" + mode); + } + + } else if (type < 41) { + + // 27 - 40 + + switch(mode) { + case QRMode.MODE_NUMBER : return 14; + case QRMode.MODE_ALPHA_NUM : return 13; + case QRMode.MODE_8BIT_BYTE : return 16; + case QRMode.MODE_KANJI : return 12; + default : + throw new Error("mode:" + mode); + } + + } else { + throw new Error("type:" + type); + } + }, + + getLostPoint : function(qrCode) { + + var moduleCount = qrCode.getModuleCount(); + + var lostPoint = 0; + + // LEVEL1 + + for (var row = 0; row < moduleCount; row++) { + + for (var col = 0; col < moduleCount; col++) { + + var sameCount = 0; + var dark = qrCode.isDark(row, col); + + for (var r = -1; r <= 1; r++) { + + if (row + r < 0 || moduleCount <= row + r) { + continue; + } + + for (var c = -1; c <= 1; c++) { + + if (col + c < 0 || moduleCount <= col + c) { + continue; + } + + if (r == 0 && c == 0) { + continue; + } + + if (dark == qrCode.isDark(row + r, col + c) ) { + sameCount++; + } + } + } + + if (sameCount > 5) { + lostPoint += (3 + sameCount - 5); + } + } + } + + // LEVEL2 + + for (var row = 0; row < moduleCount - 1; row++) { + for (var col = 0; col < moduleCount - 1; col++) { + var count = 0; + if (qrCode.isDark(row, col ) ) count++; + if (qrCode.isDark(row + 1, col ) ) count++; + if (qrCode.isDark(row, col + 1) ) count++; + if (qrCode.isDark(row + 1, col + 1) ) count++; + if (count == 0 || count == 4) { + lostPoint += 3; + } + } + } + + // LEVEL3 + + for (var row = 0; row < moduleCount; row++) { + for (var col = 0; col < moduleCount - 6; col++) { + if (qrCode.isDark(row, col) + && !qrCode.isDark(row, col + 1) + && qrCode.isDark(row, col + 2) + && qrCode.isDark(row, col + 3) + && qrCode.isDark(row, col + 4) + && !qrCode.isDark(row, col + 5) + && qrCode.isDark(row, col + 6) ) { + lostPoint += 40; + } + } + } + + for (var col = 0; col < moduleCount; col++) { + for (var row = 0; row < moduleCount - 6; row++) { + if (qrCode.isDark(row, col) + && !qrCode.isDark(row + 1, col) + && qrCode.isDark(row + 2, col) + && qrCode.isDark(row + 3, col) + && qrCode.isDark(row + 4, col) + && !qrCode.isDark(row + 5, col) + && qrCode.isDark(row + 6, col) ) { + lostPoint += 40; + } + } + } + + // LEVEL4 + + var darkCount = 0; + + for (var col = 0; col < moduleCount; col++) { + for (var row = 0; row < moduleCount; row++) { + if (qrCode.isDark(row, col) ) { + darkCount++; + } + } + } + + var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; + lostPoint += ratio * 10; + + return lostPoint; + } + +}; + + +//--------------------------------------------------------------------- +// QRMath +//--------------------------------------------------------------------- + +var QRMath = { + + glog : function(n) { + + if (n < 1) { + throw new Error("glog(" + n + ")"); + } + + return QRMath.LOG_TABLE[n]; + }, + + gexp : function(n) { + + while (n < 0) { + n += 255; + } + + while (n >= 256) { + n -= 255; + } + + return QRMath.EXP_TABLE[n]; + }, + + EXP_TABLE : new Array(256), + + LOG_TABLE : new Array(256) + +}; + +for (var i = 0; i < 8; i++) { + QRMath.EXP_TABLE[i] = 1 << i; +} +for (var i = 8; i < 256; i++) { + QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] + ^ QRMath.EXP_TABLE[i - 5] + ^ QRMath.EXP_TABLE[i - 6] + ^ QRMath.EXP_TABLE[i - 8]; +} +for (var i = 0; i < 255; i++) { + QRMath.LOG_TABLE[QRMath.EXP_TABLE[i] ] = i; +} + +//--------------------------------------------------------------------- +// QRPolynomial +//--------------------------------------------------------------------- + +function QRPolynomial(num, shift) { + + if (num.length == undefined) { + throw new Error(num.length + "/" + shift); + } + + var offset = 0; + + while (offset < num.length && num[offset] == 0) { + offset++; + } + + this.num = new Array(num.length - offset + shift); + for (var i = 0; i < num.length - offset; i++) { + this.num[i] = num[i + offset]; + } +} + +QRPolynomial.prototype = { + + get : function(index) { + return this.num[index]; + }, + + getLength : function() { + return this.num.length; + }, + + multiply : function(e) { + + var num = new Array(this.getLength() + e.getLength() - 1); + + for (var i = 0; i < this.getLength(); i++) { + for (var j = 0; j < e.getLength(); j++) { + num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i) ) + QRMath.glog(e.get(j) ) ); + } + } + + return new QRPolynomial(num, 0); + }, + + mod : function(e) { + + if (this.getLength() - e.getLength() < 0) { + return this; + } + + var ratio = QRMath.glog(this.get(0) ) - QRMath.glog(e.get(0) ); + + var num = new Array(this.getLength() ); + + for (var i = 0; i < this.getLength(); i++) { + num[i] = this.get(i); + } + + for (var i = 0; i < e.getLength(); i++) { + num[i] ^= QRMath.gexp(QRMath.glog(e.get(i) ) + ratio); + } + + // recursive call + return new QRPolynomial(num, 0).mod(e); + } +}; + +//--------------------------------------------------------------------- +// QRRSBlock +//--------------------------------------------------------------------- + +function QRRSBlock(totalCount, dataCount) { + this.totalCount = totalCount; + this.dataCount = dataCount; +} + +QRRSBlock.RS_BLOCK_TABLE = [ + + // L + // M + // Q + // H + + // 1 + [1, 26, 19], + [1, 26, 16], + [1, 26, 13], + [1, 26, 9], + + // 2 + [1, 44, 34], + [1, 44, 28], + [1, 44, 22], + [1, 44, 16], + + // 3 + [1, 70, 55], + [1, 70, 44], + [2, 35, 17], + [2, 35, 13], + + // 4 + [1, 100, 80], + [2, 50, 32], + [2, 50, 24], + [4, 25, 9], + + // 5 + [1, 134, 108], + [2, 67, 43], + [2, 33, 15, 2, 34, 16], + [2, 33, 11, 2, 34, 12], + + // 6 + [2, 86, 68], + [4, 43, 27], + [4, 43, 19], + [4, 43, 15], + + // 7 + [2, 98, 78], + [4, 49, 31], + [2, 32, 14, 4, 33, 15], + [4, 39, 13, 1, 40, 14], + + // 8 + [2, 121, 97], + [2, 60, 38, 2, 61, 39], + [4, 40, 18, 2, 41, 19], + [4, 40, 14, 2, 41, 15], + + // 9 + [2, 146, 116], + [3, 58, 36, 2, 59, 37], + [4, 36, 16, 4, 37, 17], + [4, 36, 12, 4, 37, 13], + + // 10 + [2, 86, 68, 2, 87, 69], + [4, 69, 43, 1, 70, 44], + [6, 43, 19, 2, 44, 20], + [6, 43, 15, 2, 44, 16], + + // 11 + [4, 101, 81], + [1, 80, 50, 4, 81, 51], + [4, 50, 22, 4, 51, 23], + [3, 36, 12, 8, 37, 13], + + // 12 + [2, 116, 92, 2, 117, 93], + [6, 58, 36, 2, 59, 37], + [4, 46, 20, 6, 47, 21], + [7, 42, 14, 4, 43, 15], + + // 13 + [4, 133, 107], + [8, 59, 37, 1, 60, 38], + [8, 44, 20, 4, 45, 21], + [12, 33, 11, 4, 34, 12], + + // 14 + [3, 145, 115, 1, 146, 116], + [4, 64, 40, 5, 65, 41], + [11, 36, 16, 5, 37, 17], + [11, 36, 12, 5, 37, 13], + + // 15 + [5, 109, 87, 1, 110, 88], + [5, 65, 41, 5, 66, 42], + [5, 54, 24, 7, 55, 25], + [11, 36, 12], + + // 16 + [5, 122, 98, 1, 123, 99], + [7, 73, 45, 3, 74, 46], + [15, 43, 19, 2, 44, 20], + [3, 45, 15, 13, 46, 16], + + // 17 + [1, 135, 107, 5, 136, 108], + [10, 74, 46, 1, 75, 47], + [1, 50, 22, 15, 51, 23], + [2, 42, 14, 17, 43, 15], + + // 18 + [5, 150, 120, 1, 151, 121], + [9, 69, 43, 4, 70, 44], + [17, 50, 22, 1, 51, 23], + [2, 42, 14, 19, 43, 15], + + // 19 + [3, 141, 113, 4, 142, 114], + [3, 70, 44, 11, 71, 45], + [17, 47, 21, 4, 48, 22], + [9, 39, 13, 16, 40, 14], + + // 20 + [3, 135, 107, 5, 136, 108], + [3, 67, 41, 13, 68, 42], + [15, 54, 24, 5, 55, 25], + [15, 43, 15, 10, 44, 16], + + // 21 + [4, 144, 116, 4, 145, 117], + [17, 68, 42], + [17, 50, 22, 6, 51, 23], + [19, 46, 16, 6, 47, 17], + + // 22 + [2, 139, 111, 7, 140, 112], + [17, 74, 46], + [7, 54, 24, 16, 55, 25], + [34, 37, 13], + + // 23 + [4, 151, 121, 5, 152, 122], + [4, 75, 47, 14, 76, 48], + [11, 54, 24, 14, 55, 25], + [16, 45, 15, 14, 46, 16], + + // 24 + [6, 147, 117, 4, 148, 118], + [6, 73, 45, 14, 74, 46], + [11, 54, 24, 16, 55, 25], + [30, 46, 16, 2, 47, 17], + + // 25 + [8, 132, 106, 4, 133, 107], + [8, 75, 47, 13, 76, 48], + [7, 54, 24, 22, 55, 25], + [22, 45, 15, 13, 46, 16], + + // 26 + [10, 142, 114, 2, 143, 115], + [19, 74, 46, 4, 75, 47], + [28, 50, 22, 6, 51, 23], + [33, 46, 16, 4, 47, 17], + + // 27 + [8, 152, 122, 4, 153, 123], + [22, 73, 45, 3, 74, 46], + [8, 53, 23, 26, 54, 24], + [12, 45, 15, 28, 46, 16], + + // 28 + [3, 147, 117, 10, 148, 118], + [3, 73, 45, 23, 74, 46], + [4, 54, 24, 31, 55, 25], + [11, 45, 15, 31, 46, 16], + + // 29 + [7, 146, 116, 7, 147, 117], + [21, 73, 45, 7, 74, 46], + [1, 53, 23, 37, 54, 24], + [19, 45, 15, 26, 46, 16], + + // 30 + [5, 145, 115, 10, 146, 116], + [19, 75, 47, 10, 76, 48], + [15, 54, 24, 25, 55, 25], + [23, 45, 15, 25, 46, 16], + + // 31 + [13, 145, 115, 3, 146, 116], + [2, 74, 46, 29, 75, 47], + [42, 54, 24, 1, 55, 25], + [23, 45, 15, 28, 46, 16], + + // 32 + [17, 145, 115], + [10, 74, 46, 23, 75, 47], + [10, 54, 24, 35, 55, 25], + [19, 45, 15, 35, 46, 16], + + // 33 + [17, 145, 115, 1, 146, 116], + [14, 74, 46, 21, 75, 47], + [29, 54, 24, 19, 55, 25], + [11, 45, 15, 46, 46, 16], + + // 34 + [13, 145, 115, 6, 146, 116], + [14, 74, 46, 23, 75, 47], + [44, 54, 24, 7, 55, 25], + [59, 46, 16, 1, 47, 17], + + // 35 + [12, 151, 121, 7, 152, 122], + [12, 75, 47, 26, 76, 48], + [39, 54, 24, 14, 55, 25], + [22, 45, 15, 41, 46, 16], + + // 36 + [6, 151, 121, 14, 152, 122], + [6, 75, 47, 34, 76, 48], + [46, 54, 24, 10, 55, 25], + [2, 45, 15, 64, 46, 16], + + // 37 + [17, 152, 122, 4, 153, 123], + [29, 74, 46, 14, 75, 47], + [49, 54, 24, 10, 55, 25], + [24, 45, 15, 46, 46, 16], + + // 38 + [4, 152, 122, 18, 153, 123], + [13, 74, 46, 32, 75, 47], + [48, 54, 24, 14, 55, 25], + [42, 45, 15, 32, 46, 16], + + // 39 + [20, 147, 117, 4, 148, 118], + [40, 75, 47, 7, 76, 48], + [43, 54, 24, 22, 55, 25], + [10, 45, 15, 67, 46, 16], + + // 40 + [19, 148, 118, 6, 149, 119], + [18, 75, 47, 31, 76, 48], + [34, 54, 24, 34, 55, 25], + [20, 45, 15, 61, 46, 16] +]; + +QRRSBlock.getRSBlocks = function(typeNumber, errorCorrectLevel) { + + var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel); + + if (rsBlock == undefined) { + throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel); + } + + var length = rsBlock.length / 3; + + var list = new Array(); + + for (var i = 0; i < length; i++) { + + var count = rsBlock[i * 3 + 0]; + var totalCount = rsBlock[i * 3 + 1]; + var dataCount = rsBlock[i * 3 + 2]; + + for (var j = 0; j < count; j++) { + list.push(new QRRSBlock(totalCount, dataCount) ); + } + } + + return list; +} + +QRRSBlock.getRsBlockTable = function(typeNumber, errorCorrectLevel) { + + switch(errorCorrectLevel) { + case QRErrorCorrectLevel.L : + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; + case QRErrorCorrectLevel.M : + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; + case QRErrorCorrectLevel.Q : + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; + case QRErrorCorrectLevel.H : + return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; + default : + return undefined; + } +} + +//--------------------------------------------------------------------- +// QRBitBuffer +//--------------------------------------------------------------------- + +function QRBitBuffer() { + this.buffer = new Array(); + this.length = 0; +} + +QRBitBuffer.prototype = { + + get : function(index) { + var bufIndex = Math.floor(index / 8); + return ( (this.buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1; + }, + + put : function(num, length) { + for (var i = 0; i < length; i++) { + this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1); + } + }, + + getLengthInBits : function() { + return this.length; + }, + + putBit : function(bit) { + + var bufIndex = Math.floor(this.length / 8); + if (this.buffer.length <= bufIndex) { + this.buffer.push(0); + } + + if (bit) { + this.buffer[bufIndex] |= (0x80 >>> (this.length % 8) ); + } + + this.length++; + } +}; diff --git a/public/jquery.storage.js/.gitattributes b/public/jquery.storage.js/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/public/jquery.storage.js/.gitattributes @@ -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 diff --git a/public/jquery.storage.js/.gitignore b/public/jquery.storage.js/.gitignore new file mode 100644 index 0000000..b9d6bd9 --- /dev/null +++ b/public/jquery.storage.js/.gitignore @@ -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 diff --git a/public/jquery.storage.js/jquery.storage.js b/public/jquery.storage.js/jquery.storage.js new file mode 100644 index 0000000..4894610 --- /dev/null +++ b/public/jquery.storage.js/jquery.storage.js @@ -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)); \ No newline at end of file diff --git a/public/jquery.storage.js/localstorage.jquery.json b/public/jquery.storage.js/localstorage.jquery.json new file mode 100644 index 0000000..9eb4402 --- /dev/null +++ b/public/jquery.storage.js/localstorage.jquery.json @@ -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" + } +} \ No newline at end of file diff --git a/public/jquery.storage.js/sessionstorage.jquery.json b/public/jquery.storage.js/sessionstorage.jquery.json new file mode 100644 index 0000000..5817b37 --- /dev/null +++ b/public/jquery.storage.js/sessionstorage.jquery.json @@ -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" + } +} \ No newline at end of file diff --git a/public/jquery.storage.js/storage.jquery.json b/public/jquery.storage.js/storage.jquery.json new file mode 100644 index 0000000..296742c --- /dev/null +++ b/public/jquery.storage.js/storage.jquery.json @@ -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" + } +} \ No newline at end of file diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..e6a1acb --- /dev/null +++ b/public/manifest.json @@ -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" +} diff --git a/public/print.css b/public/print.css new file mode 100644 index 0000000..99103cd --- /dev/null +++ b/public/print.css @@ -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; +} diff --git a/public/qifi-small.png b/public/qifi-small.png new file mode 100644 index 0000000000000000000000000000000000000000..209e4d02a41bbde4874f08d206c499fbcf77e45e GIT binary patch literal 1103 zcmV-V1hD&wP)v00004b3#c}2nYxW zd<{98FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11I$T8K~z|U)t5U+Gg%bJ|1(Yqfgt!m2}bb|CHMjjgoS{Pg@OZF z6wyLhw2(A5Ut=Ly2sVq@q_PS&7M3duX+(lZOyLs(iJC+N5dy}-C(%iAzQT3PBs2LE zqkiijF5EeD4)-_r%zYdX5z-rgzX8y1Y(FxYSSU+Wv46Inl#FVS^obz3lu|OPLH@7w zEg(6DVfYMN9ey7?3&51g#Ng%sOJ&%YVn$*Z1{}w|H~e*#gNTqwB-nK0hc!VE=p*Fw z`3L}gkAL!44lK)}rltm0S67I~H+|`T&{v4*4EY#jYgr@>oGMorRbxW zvXPMyIy^k2csx!-M9E~5B9REq&CQWUqmlVKolYr5b#-+l2m%FzLDFiqs?FhWNHJEc zl>m^zU{FlA;!LirtYC6-QhF9e5gLsKjg5_1SXjX5=qPM98}9G#0RW*;2wPiQ0Dz~b zmqUYyq`s0w@$FFS?(T+8r$c3BrK-PdR_u1W^d;x#=VY~7iDg-;t*xbvjScCY)9I9A zs;a8UY&MhGY$lH5NDu@H1On1~i^W1dpHJ2zk%(-ZU@%CbP)KnmvmkvwpX6k>+hy-6 zDk|vl@li^DX=$lyy3-;1`ucLN;)2r6Fbvw;+og24-EP^GUteEidwUxL0|Q`L7A-9; z*xTDvP0_3Tem{nWhGe#xnHkt@wqM2q8LQi1FhH-@0|27YD3Zx!R>0-urPOa~YRU^T z7K>qheO+c7A0N*PFbgskiy@Infa5rnm6a(5l$V!F{kyxnyauVSugAp1gv{2~*7iQg z=jUgH!(o`sX6W^Lw6?Y)5D2_Ea~v!di}ZYae4N)H4Gj&LpP$bgDpOe}&g$x_)L&X! zf=;KCo@ZxgVKf>60GpeeI6FIg_rQfHYoADI&#l-~?5xL!N;y6y`XEK?H=Xv6Jp0rvm34%Zm4-e$=c*x`N(9zLR&V6!!f1f-a z5AE#iXDwSg5@i@l-7|L+^3&5W!aF#zv z&j3~d{F`p%i0Gem1H&+XK&43k0wn?XGnu|cjq|M`i>kYekRp#7q~;Sra!GMZ_z5O= VR8YR6?fL)!002ovPDHLkV1iGN^Zx(< literal 0 HcmV?d00001 diff --git a/public/qifi.png b/public/qifi.png new file mode 100644 index 0000000000000000000000000000000000000000..c03cb259e90b8cbb0be6eba5db7c6f839ddb1001 GIT binary patch literal 10265 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 + + + + + + + + + image/svg+xml + + + + + + + + + + QiFi + + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..c2a49f4 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / diff --git a/public/style.css b/public/style.css new file mode 100644 index 0000000..4daf98f --- /dev/null +++ b/public/style.css @@ -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; } +} diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..9640a59 --- /dev/null +++ b/public/sw.js @@ -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; + }); + }) + ); +}); \ No newline at end of file