diff --git a/.github/codeql-config.yml b/.github/codeql-config.yml new file mode 100644 index 00000000..574f9b33 --- /dev/null +++ b/.github/codeql-config.yml @@ -0,0 +1,4 @@ +name: CodeQL Config + +paths-ignore: + - lib diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..e9c8e05d --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,38 @@ +name: CodeQL + +on: + push: + branches: [nightly] + pull_request: + branches: [nightly] + schedule: + - cron: '05 10 * * 1' + +jobs: + codeql-analysis: + name: CodeQL Analysis + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ['javascript', 'python'] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + config-file: ./.github/codeql-config.yml + languages: ${{ matrix.language }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/issues-stale.yml b/.github/workflows/issues-stale.yml index 0643cb0a..b805e266 100644 --- a/.github/workflows/issues-stale.yml +++ b/.github/workflows/issues-stale.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Stale - uses: actions/stale@v7 + uses: actions/stale@v9 with: stale-issue-message: > This issue is stale because it has been open for 30 days with no activity. @@ -30,7 +30,7 @@ jobs: days-before-close: 5 - name: Invalid Template - uses: actions/stale@v7 + uses: actions/stale@v9 with: stale-issue-message: > Invalid issues template. diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index 34ceb357..a60987f5 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -10,6 +10,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Label Issues - uses: dessant/label-actions@v3 + uses: dessant/label-actions@v4 with: github-token: ${{ github.token }} diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index 6480575f..62c3f86c 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -13,7 +13,7 @@ jobs: if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Prepare id: prepare @@ -33,21 +33,20 @@ jobs: echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT fi echo "commit=${GITHUB_SHA}" >> $GITHUB_OUTPUT - echo "build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT echo "docker_platforms=linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v6" >> $GITHUB_OUTPUT echo "docker_image=${{ secrets.DOCKER_REPO }}/tautulli" >> $GITHUB_OUTPUT - name: Set Up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 id: buildx with: version: latest - name: Cache Docker Layers - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} @@ -55,22 +54,28 @@ jobs: ${{ runner.os }}-buildx- - name: Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v3 if: success() with: username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + password: ${{ secrets.DOCKER_TOKEN }} - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 if: success() with: registry: ghcr.io username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.GHCR_TOKEN }} + - name: Extract Docker Metadata + id: metadata + uses: docker/metadata-action@v5 + with: + images: ${{ steps.prepare.outputs.docker_image }} + - name: Docker Build and Push - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v6 if: success() with: context: . @@ -81,10 +86,10 @@ jobs: TAG=${{ steps.prepare.outputs.tag }} BRANCH=${{ steps.prepare.outputs.branch }} COMMIT=${{ steps.prepare.outputs.commit }} - BUILD_DATE=${{ steps.prepare.outputs.build_date }} tags: | ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }} ghcr.io/${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.tag }} + labels: ${{ steps.metadata.outputs.labels }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache @@ -94,23 +99,10 @@ jobs: if: always() && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-latest steps: - - name: Get Build Job Status - uses: technote-space/workflow-conclusion-action@v3.0 - - - name: Combine Job Status - id: status - run: | - failures=(neutral, skipped, timed_out, action_required) - if [[ ${array[@]} =~ $WORKFLOW_CONCLUSION ]]; then - echo "status=failure" >> $GITHUB_OUTPUT - else - echo "status=$WORKFLOW_CONCLUSION" >> $GITHUB_OUTPUT - fi - - name: Post Status to Discord uses: sarisia/actions-status-discord@v1 with: webhook: ${{ secrets.DISCORD_WEBHOOK }} - status: ${{ steps.status.outputs.status }} + status: ${{ needs.build-docker.result == 'success' && 'success' || contains(needs.*.result, 'failure') && 'failure' || 'cancelled' }} title: ${{ github.workflow }} nofail: true diff --git a/.github/workflows/publish-installers.yml b/.github/workflows/publish-installers.yml index 31ad0f81..b4a66960 100644 --- a/.github/workflows/publish-installers.yml +++ b/.github/workflows/publish-installers.yml @@ -6,10 +6,13 @@ on: branches: [master, beta, nightly] tags: [v*] +env: + PYTHON_VERSION: '3.11' + jobs: build-installer: name: Build ${{ matrix.os_upper }} Installer - runs-on: ${{ matrix.os }}-latest + runs-on: ${{ matrix.os }}-${{ matrix.os_version }} if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} strategy: fail-fast: false @@ -17,14 +20,18 @@ jobs: include: - os: 'windows' os_upper: 'Windows' + os_version: 'latest' + arch: 'x64' ext: 'exe' - os: 'macos' os_upper: 'MacOS' + os_version: '14' + arch: 'universal' ext: 'pkg' steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set Release Version id: get_version @@ -52,29 +59,29 @@ jobs: echo $GITHUB_SHA > version.txt - name: Set Up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: ${{ env.PYTHON_VERSION }} cache: pip cache-dependency-path: '**/requirements*.txt' - name: Install Dependencies run: | python -m pip install --upgrade pip - pip install -r package/requirements-package.txt + pip install -r package/requirements-package.txt --no-binary cffi - name: Build Package run: | pyinstaller -y ./package/Tautulli-${{ matrix.os }}.spec - name: Create Windows Installer - uses: joncloud/makensis-action@v3.7 + uses: joncloud/makensis-action@v4.1 if: matrix.os == 'windows' with: script-file: ./package/Tautulli.nsi arguments: > /DVERSION=${{ steps.get_version.outputs.VERSION_NSIS }} - /DINSTALLER_NAME=..\Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe + /DINSTALLER_NAME=..\Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-${{ matrix.arch }}.${{ matrix.ext }} additional-plugin-paths: package/nsis-plugins - name: Create MacOS Installer @@ -85,13 +92,31 @@ jobs: --version ${{ steps.get_version.outputs.VERSION }} \ --component ./dist/Tautulli.app \ --scripts ./package/macos-scripts \ - Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg + Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-${{ matrix.arch }}.${{ matrix.ext }} - name: Upload Installer - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Tautulli-${{ matrix.os }}-installer - path: Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.${{ matrix.ext }} + path: Tautulli-${{ matrix.os }}-${{ steps.get_version.outputs.RELEASE_VERSION }}-${{ matrix.arch }}.${{ matrix.ext }} + + virus-total: + name: VirusTotal Scan + needs: build-installer + if: needs.build-installer.result == 'success' && !contains(github.event.head_commit.message, '[skip ci]') + runs-on: ubuntu-latest + steps: + - name: Download Installers + if: needs.build-installer.result == 'success' + uses: actions/download-artifact@v4 + + - name: Upload to VirusTotal + uses: crazy-max/ghaction-virustotal@v4 + with: + vt_api_key: ${{ secrets.VT_API_KEY }} + files: | + Tautulli-windows-installer/Tautulli-windows-*-x64.exe + Tautulli-macos-installer/Tautulli-macos-*-universal.pkg release: name: Release Installers @@ -99,11 +124,8 @@ jobs: if: always() && startsWith(github.ref, 'refs/tags/') && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-latest steps: - - name: Get Build Job Status - uses: technote-space/workflow-conclusion-action@v3.0 - - name: Checkout Code - uses: actions/checkout@v3.2.0 + uses: actions/checkout@v4 - name: Set Release Version id: get_version @@ -111,52 +133,35 @@ jobs: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT - name: Download Installers - if: env.WORKFLOW_CONCLUSION == 'success' - uses: actions/download-artifact@v3 + if: needs.build-installer.result == 'success' + uses: actions/download-artifact@v4 - name: Get Changelog id: get_changelog run: | CHANGELOG="$( sed -n '/^## /{p; :loop n; p; /^## /q; b loop}' CHANGELOG.md \ | sed '$d' | sed '$d' | sed '$d' )" - echo "CHANGELOG=${CHANGELOG}" >> $GITHUB_OUTPUT + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "CHANGELOG<<$EOF" >> $GITHUB_OUTPUT + echo "$CHANGELOG" >> $GITHUB_OUTPUT + echo "$EOF" >> $GITHUB_OUTPUT - name: Create Release - uses: actions/create-release@v1 + uses: softprops/action-gh-release@v2 id: create_release env: GITHUB_TOKEN: ${{ secrets.GHACTIONS_TOKEN }} with: tag_name: ${{ steps.get_version.outputs.RELEASE_VERSION }} - release_name: Tautulli ${{ steps.get_version.outputs.RELEASE_VERSION }} + name: Tautulli ${{ steps.get_version.outputs.RELEASE_VERSION }} body: | ## Changelog ##${{ steps.get_changelog.outputs.CHANGELOG }} - draft: false prerelease: ${{ endsWith(steps.get_version.outputs.RELEASE_VERSION, '-beta') }} - - - name: Upload Windows Installer - uses: actions/upload-release-asset@v1 - if: env.WORKFLOW_CONCLUSION == 'success' - env: - GITHUB_TOKEN: ${{ secrets.GHACTIONS_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: Tautulli-windows-installer/Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe - asset_name: Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe - asset_content_type: application/vnd.microsoft.portable-executable - - - name: Upload MacOS Installer - uses: actions/upload-release-asset@v1 - if: env.WORKFLOW_CONCLUSION == 'success' - env: - GITHUB_TOKEN: ${{ secrets.GHACTIONS_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: Tautulli-macos-installer/Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg - asset_name: Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.pkg - asset_content_type: application/vnd.apple.installer+xml + files: | + Tautulli-windows-installer/Tautulli-windows-${{ steps.get_version.outputs.RELEASE_VERSION }}-x64.exe + Tautulli-macos-installer/Tautulli-macos-${{ steps.get_version.outputs.RELEASE_VERSION }}-universal.pkg discord: name: Discord Notification @@ -164,23 +169,10 @@ jobs: if: always() && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-latest steps: - - name: Get Build Job Status - uses: technote-space/workflow-conclusion-action@v3.0 - - - name: Combine Job Status - id: status - run: | - failures=(neutral, skipped, timed_out, action_required) - if [[ ${array[@]} =~ $WORKFLOW_CONCLUSION ]]; then - echo "status=failure" >> $GITHUB_OUTPUT - else - echo "status=$WORKFLOW_CONCLUSION" >> $GITHUB_OUTPUT - fi - - name: Post Status to Discord uses: sarisia/actions-status-discord@v1 with: webhook: ${{ secrets.DISCORD_WEBHOOK }} - status: ${{ steps.status.outputs.status }} + status: ${{ needs.build-installer.result == 'success' && 'success' || contains(needs.*.result, 'failure') && 'failure' || 'cancelled' }} title: ${{ github.workflow }} nofail: true diff --git a/.github/workflows/publish-snap.yml b/.github/workflows/publish-snap.yml index 9df4d2fd..b3898a38 100644 --- a/.github/workflows/publish-snap.yml +++ b/.github/workflows/publish-snap.yml @@ -20,7 +20,7 @@ jobs: - armhf steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Prepare id: prepare @@ -35,22 +35,22 @@ jobs: fi - name: Set Up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Build Snap Package - uses: diddlesnaps/snapcraft-multiarch-action@v1 + uses: diddlesnaps/snapcraft-multiarch-action@master id: build with: architecture: ${{ matrix.architecture }} - name: Upload Snap Package - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Tautulli-snap-package-${{ matrix.architecture }} path: ${{ steps.build.outputs.snap }} - name: Review Snap Package - uses: diddlesnaps/snapcraft-review-tools-action@v1 + uses: diddlesnaps/snapcraft-review-tools-action@master with: snap: ${{ steps.build.outputs.snap }} @@ -69,23 +69,10 @@ jobs: if: always() && !contains(github.event.head_commit.message, '[skip ci]') runs-on: ubuntu-latest steps: - - name: Get Build Job Status - uses: technote-space/workflow-conclusion-action@v3.0 - - - name: Combine Job Status - id: status - run: | - failures=(neutral, skipped, timed_out, action_required) - if [[ ${array[@]} =~ $WORKFLOW_CONCLUSION ]]; then - echo "status=failure" >> $GITHUB_OUTPUT - else - echo "status=$WORKFLOW_CONCLUSION" >> $GITHUB_OUTPUT - fi - - name: Post Status to Discord uses: sarisia/actions-status-discord@v1 with: webhook: ${{ secrets.DISCORD_WEBHOOK }} - status: ${{ steps.status.outputs.status }} + status: ${{ needs.build-snap.result == 'success' && 'success' || contains(needs.*.result, 'failure') && 'failure' || 'cancelled' }} title: ${{ github.workflow }} nofail: true diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 58cb4ee4..ac550fe2 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Comment on Pull Request uses: mshick/add-pr-comment@v2 @@ -18,7 +18,6 @@ jobs: with: message: Pull requests must be made to the `nightly` branch. Thanks. repo-token: ${{ secrets.GITHUB_TOKEN }} - repo-token-user-login: 'github-actions[bot]' - name: Fail Workflow if: github.base_ref != 'nightly' diff --git a/.github/workflows/submit-winget.yml b/.github/workflows/submit-winget.yml index 9472b7fb..efa6cee7 100644 --- a/.github/workflows/submit-winget.yml +++ b/.github/workflows/submit-winget.yml @@ -9,8 +9,13 @@ jobs: winget: name: Submit Winget Package runs-on: windows-latest - if: !github.event.release.prerelease + if: ${{ !github.event.release.prerelease }} steps: + - name: Sync Winget Fork + run: gh repo sync ${{ secrets.WINGET_USERNAME }}/winget-pkgs -b master + env: + GH_TOKEN: ${{ secrets.WINGET_TOKEN }} + - name: Submit package to Windows Package Manager Community Repository run: | $wingetPackage = "Tautulli.Tautulli" @@ -23,3 +28,17 @@ jobs: # getting latest wingetcreate file iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe .\wingetcreate.exe update $wingetPackage -s -v $version -u $installerUrl -t $gitToken + + virus-total: + name: VirusTotal Scan + runs-on: ubuntu-latest + steps: + - name: Upload to VirusTotal + uses: crazy-max/ghaction-virustotal@v4 + with: + vt_api_key: ${{ secrets.VT_API_KEY }} + github_token: ${{ secrets.GHACTIONS_TOKEN }} + update_release_body: true + files: | + .exe$ + .pkg$ diff --git a/.gitignore b/.gitignore index 68d5abaf..1e54132b 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,9 @@ Thumbs.db #Ignore files generated by PyCharm *.idea/* +#Ignore files generated by VSCode +*.vscode/* + #Ignore files generated by vi *.swp diff --git a/CHANGELOG.md b/CHANGELOG.md index d99eb976..b349b355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,277 @@ # Changelog -## v2.12.0-beta (2023-03-03) +## v2.15.3 (2025-08-03) + +* Exporter: + * New: Added hearingImpaired for subtitles and visualImpaired for audio attributes to exporter fields. +* Graphs: + * Fix: Remove duplicate "Total" entry in graph tooltips. (Thanks @zdimension) (#2534) +* UI: + * Fix: Failing to retrieve collections / playlists with over 1000 items. + * Fix: Scrollbar not showing on macosx and webkit browsers. (#2221) + * Fix: Incorrect rounding of minutes in global stats play duration. + * Fix: Disable browser autocomplete for notification agent and newsletter agent configurations. (#2557) +* API: + * New: Added ability to return svg files using pms_image_proxy API command. +* Other: + * New: Added ability to set config values using environment variables. (Thanks @komuw) (#2309, #2543) + + +## v2.15.2 (2025-04-12) + +* Activity: + * New: Added link to library by clicking media type icon. + * New: Added stream count to tab title on homepage. (#2517) +* History: + * Fix: Check stream watched status before stream stopped status. (#2506) +* Notifications: + * Fix: ntfy notifications failing to send if provider link is blank. + * Fix: Check Pushover notification attachment is under 5MB limit. (#2396) + * Fix: Track URLs redirecting to the correct media page. (#2513) + * New: Added audio profile notification parameters. + * New: Added PATCH method for Webhook notifications. +* Graphs: + * New: Added Total line to daily streams graph. (Thanks @zdimension) (#2497) +* UI: + * Fix: Do not redirect API requests to the login page. (#2490) + * Change: Swap source and stream columns in stream info modal. +* Other: + * Fix: Various typos. (Thanks @luzpaz) (#2520) + * Fix: CherryPy CORS response header not being set correctly. (#2279) + + +## v2.15.1 (2025-01-11) + +* Activity: + * Fix: Detection of HDR transcodes. (Thanks @cdecker08) (#2412, #2466) +* Newsletters: + * Fix: Disable basic authentication for /newsletter and /image endpoints. (#2472) +* Exporter: + * New: Added logos to season and episode exports. +* Other: + * Fix: Docker container https health check. + + +## v2.15.0 (2024-11-24) + +* Notes: + * Support for Python 3.8 has been dropped. The minimum Python version is now 3.9. +* Notifications: + * New: Allow Telegram blockquote and tg-emoji HTML tags. (Thanks @MythodeaLoL) (#2427) + * New: Added Plex slug and Plex Watch URL notification parameters. (#2420) + * Change: Update OneSignal API calls to use the new API endpoint for Tautulli Remote App notifications. +* Newsletters: + * Fix: Dumping custom dates in raw newsletter json. +* History: + * Fix: Unable to fix match for artists. (#2429) +* Exporter: + * New: Added movie and episode hasVoiceActivity attribute to exporter fields. + * New: Added subtitle canAutoSync attribute to exporter fields. + * New: Added logos to the exporter fields. +* UI: + * New: Add friendly name to the top bar of config modals. (Thanks @peagravel) (#2432) +* API: + * New: Added plex slugs to metadata in the get_metadata API command. +* Other: + * Fix: Tautulli failing to start with Python 3.13. (#2426) + + +## v2.14.6 (2024-10-12) + +* Newsletters: + * Fix: Allow formatting newsletter date parameters. + * Change: Support apscheduler compatible cron expressions. +* UI: + * Fix: Round runtime before converting to human duration. + * Fix: Make recently added/watched rows touch scrollable. +* Other: + * Fix: Auto-updater not running. + + +## v2.14.5 (2024-09-20) + +* Activity: + * Fix: Display of 2k resolution on activity card. +* Notifications: + * Fix: ntfy notifications with special characters failing to send. +* Other: + * Fix: Memory leak with database closing. (#2404) + + +## v2.14.4 (2024-08-10) + +* Notifications: + * Fix: Update Slack notification info card. + * New: Added ntfy notification agent. (Thanks @nwithan8) (#2356, #2000) +* UI: + * Fix: macOS platform capitalization. +* Other: + * Fix: Remove deprecated getdefaultlocale. (Thanks @teodorstelian) (#2364, #2345) + + +## v2.14.3 (2024-06-19) + +* Graphs: + * Fix: History table not loading when clicking on the graphs in some instances. +* UI: + * Fix: Scheduled tasks table not loading when certain tasks are disabled. + * Removed: Unnecessary Remote Server checkbox from the settings page. +* Other: + * Fix: Webserver not restarting after the setup wizard. + * Fix: Workaround webserver crashing in some instances. + + +## v2.14.2 (2024-05-18) + +* History: + * Fix: Live TV activity not logging to history. + * Fix: Incorrect grouping of live TV history. +* Notifications: + * Fix: Pushover configuration settings refreshing after entering a token. + * Fix: Plex remote access down notifications not triggering. + * Fix: Deleting all images from Cloudinary only deleting 1000 images. + * New: Added platform version and product version notification parameters. (#2244) + * New: Added LAN streams and WAN streams notification parameters. (#2276) + * New: Added Dolby Vision notification parameters. (#2240) + * New: Added live TV channel notification parameters. + * Change: Improved Tautulli Remote App notification encryption method. + * Note: Requires Tautulli Remote App version 3.2.4. +* Exporter: + * New: Added slug attribute to exporter fields. + * New: Added track genres to exporter fields. + * New: Added playlist source URI to exporter fields. + * New: Added artProvider and thumbProvider to exporter fields. +* UI: + * Fix: Mask deleted usernames in the logs. + * Fix: Live TV watch stats not showing on the media info page. + * Fix: Users without access to Plex server not showing as inactive. + * Removed: Deprecated synced item pages. + * Removed: Anonymous redirect settings. Links now use browser no-referrer policy instead. +* API: + * New: Added Dolby Vision info to the get_metadata API command. + * New: Added before and after parameters to the get_home_stats API command. (#2231) +* Packages: + * New: Universal binary for macOS for Apple silicon. + * New: Bump Snap package to core22. +* Other: + * Change: Login cookie expires changed to max-age. + * Change: Improved key generation for login password. It is recommended to reenter your HTTP Password in the settings after upgrading. + * Removed: Python 2 compatibility code. (#2098, #2226) (Thanks @zdimension) + + +## v2.13.4 (2023-12-07) + +* UI: + * Fix: Tautulli configuration settings page not loading when system language is None. + * Fix: Login cookie expiring too quickly. + + +## v2.13.3 (2023-12-03) + +* Notifications: + * New: Added duration_time notification parameter. + * New: Added file_size_bytes notification parameter. + * New: Added time formats notification text modifiers. + * New: Added support for thetvdb_url for movies. +* UI: + * Fix: Activity card overflowing due to screen scaling. (#2033) + * Fix: Stream duration on activity card not being updated on track changes in some cases. (#2206) + + +## v2.13.2 (2023-10-26) + +* History: + * New: Added quarter values icons for history watch status. (#2179, #2156) (Thanks @herby2212) +* Graphs: + * New: Added concurrent streams per day graph. (#2046) (Thanks @herby2212) +* Exporter: + * New: Added metadata directory to exporter fields. + * Removed: Banner exporter fields for tv shows. +* UI: + * New: Added last triggered time to notification agents and newsletter agent lists. +* Other: + * New: Added X-Plex-Language header override to config file. + + +## v2.13.1 (2023-08-25) + +* Notes: + * Support for Python 3.7 has been dropped. The minimum Python version is now 3.8. +* Other: + * Fix: Tautulli failing to start on some systems. + + +## v2.13.0 (2023-08-25) + +* Notes: + * Support for Python 3.7 has been dropped. The minimum Python version is now 3.8. +* Notifications: + * Fix: Improved watched notification trigger description. (#2104) + * New: Added notification image option for iOS Tautulli Remote app. +* Exporter: + * New: Added track chapter export fields. + * New: Added on-demand subtitle export fields. + + +## v2.12.5 (2023-07-13) + +* Activity: + * New: Added d3d11va to list of hardware decoders. +* History: + * Fix: Incorrect grouping of play history. + * New: Added button in settings to regroup play history. +* Notifications: + * Fix: Incorrect concurrent streams notifications by IP addresss for IPv6 addresses (#2096) (Thanks @pooley182) +* UI: + * Fix: Occasional UI crashing on Python 3.11. + * New: Added multiselect user filters to History and Graphs pages. (#2090) (Thanks @zdimension) +* API: + * New: Added regroup_history API command. + * Change: Updated graph API commands to accept a comma separated list of user IDs. + + +## v2.12.4 (2023-05-23) + +* History: + * Fix: Set view offset equal to duration if a stream is stopped within the last 10 sec. +* Other: + * Fix: Database import may fail for some older databases. + * Fix: Double-quoted strings for newer versions of SQLite. (#2015, #2057) +* API: + * Change: Return the ID for async API calls (export_metadata, notify, notify_newsletter). + + +## v2.12.3 (2023-04-14) + +* Activity: + * Fix: Incorrect subtitle decision shown when subtitles are transcoded. +* History: + * Fix: Incorrect order when sorting by the duration column in the history tables. +* Notifications: + * Fix: Logging error when running scripts that use PlexAPI. +* UI: + * Fix: Calculate file sizes setting causing the media info table to fail to load. + * Fix: Incorrect artwork and thumbnail shown for Live TV on the Most Active Libraries statistics card. +* API: + * Change: Renamed duration to play_duration in the get_history API response. (Note: duration kept for backwards compatibility.) + + +## v2.12.2 (2023-03-16) + +* Other: + * Fix: Tautulli not starting on FreeBSD jails. + + +## v2.12.1 (2023-03-14) + +* Activity: + * Fix: Stop checking for deprecated sync items sessions. + * Change: Do not show audio language on activity cards for music. +* Other: + * Fix: Tautulli not starting on macOS. + + +## v2.12.0 (2023-03-13) * Notifications: * New: Added support for Telegram group topics. (#1980) @@ -13,18 +284,21 @@ * Change: Trigger watched notifications based on the video watched completion behaviour setting. * Exporter: * Fix: Unable to run exporter when using the Snap package. (#2007) + * New: Added credits marker, and audio/subtitle settings to export fields. * UI: + * Fix: Incorrect styling and missing content for collection media info pages. * New: Added edition details field on movie media info pages. (#1957) (Thanks @herby2212) * New: Added setting to change the video watched completion behaviour. * New: Added watch time and user statistics to collection and playlist media info pages. (#1982, #2012) (Thanks @herby2212) * New: Added history table to collection and playlist media info pages. * New: Dynamically change watched status in the UI based on video watched completion behaviour setting. - * Fix: Incorrect styling and missing content for collection media info pages. + * New: Added hidden setting to override server name. * Change: Move track artist to a details field instead of in the title on track media info pages. * API: * New: Added section_id and user_id parameters to get_home_stats API command. (#1944) * New: Added marker info to get_metadata API command results. * New: Added media_type parameter to get_item_watch_time_stats and get_item_user_stats API commands. (#1982) (Thanks @herby2212) + * New: Added last_refreshed timestamp to get_library_media_info API command response. * Other: * Change: Migrate analytics to Google Analytics 4. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19644c56..46a644e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ All pull requests should be based on the `nightly` branch, to minimize cross mer ### Python Code #### Compatibility -The code should work with Python 3.7+. Note that Tautulli runs on many different platforms. +The code should work with Python 3.8+. Note that Tautulli runs on many different platforms. Re-use existing code. Do not hesitate to add logging in your code. You can the logger module `plexpy.logger.*` for this. Web requests are invoked via `plexpy.request.*` and derived ones. Use these methods to automatically add proper and meaningful error handling. diff --git a/Dockerfile b/Dockerfile index 7a52841f..8d8c324b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,4 +25,4 @@ CMD [ "python", "Tautulli.py", "--datadir", "/config" ] ENTRYPOINT [ "./start.sh" ] EXPOSE 8181 -HEALTHCHECK --start-period=90s CMD curl -ILfSs http://localhost:8181/status > /dev/null || curl -ILfkSs https://localhost:8181/status > /dev/null || exit 1 +HEALTHCHECK --start-period=90s CMD curl -ILfks https://localhost:8181/status > /dev/null || curl -ILfs http://localhost:8181/status > /dev/null || exit 1 diff --git a/README.md b/README.md index d38f69e1..37829290 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ and [PlexWatchWeb](https://github.com/ecleese/plexWatchWeb). [![Docker Stars][badge-docker-stars]][DockerHub] [![Downloads][badge-downloads]][Releases Latest] -[badge-python]: https://img.shields.io/badge/python->=3.7-blue?style=flat-square +[badge-python]: https://img.shields.io/badge/python->=3.9-blue?style=flat-square [badge-docker-pulls]: https://img.shields.io/docker/pulls/tautulli/tautulli?style=flat-square [badge-docker-stars]: https://img.shields.io/docker/stars/tautulli/tautulli?style=flat-square [badge-downloads]: https://img.shields.io/github/downloads/Tautulli/Tautulli/total?style=flat-square @@ -129,7 +129,7 @@ This is free software under the GPL v3 open source license. Feel free to do with but any modification must be open sourced. A copy of the license is included. This software includes Highsoft software libraries which you may freely distribute for -non-commercial use. Commerical users must licence this software, for more information visit +non-commercial use. Commercial users must licence this software, for more information visit https://shop.highsoft.com/faq/non-commercial#non-commercial-redistribution. diff --git a/Tautulli.py b/Tautulli.py index eebfa55a..b3cf4736 100755 --- a/Tautulli.py +++ b/Tautulli.py @@ -23,18 +23,18 @@ import sys # Ensure lib added to path, before any other imports sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib')) -from future.builtins import str -import appdirs import argparse import datetime import locale +import platformdirs import pytz import signal import shutil import time import threading import tzlocal +import ctypes import plexpy from plexpy import common, config, database, helpers, logger, webstart @@ -70,8 +70,26 @@ def main(): plexpy.SYS_ENCODING = None try: - locale.setlocale(locale.LC_ALL, "") - plexpy.SYS_LANGUAGE, plexpy.SYS_ENCODING = locale.getdefaultlocale() + + # Attempt to get the system's locale settings + language_code, encoding = locale.getlocale() + + # Special handling for Windows platform + if sys.platform == 'win32': + # Get the user's current language settings on Windows + windll = ctypes.windll.kernel32 + lang_id = windll.GetUserDefaultLCID() + + # Map Windows language ID to locale identifier + language_code = locale.windows_locale.get(lang_id, '') + + # Get the preferred encoding + encoding = locale.getpreferredencoding() + + # Assign values to application-specific variable + plexpy.SYS_LANGUAGE = language_code + plexpy.SYS_ENCODING = encoding + except (locale.Error, IOError): pass @@ -111,7 +129,7 @@ def main(): if args.quiet: plexpy.QUIET = True - # Do an intial setup of the logger. + # Do an initial setup of the logger. # Require verbose for pre-initilization to see critical errors logger.initLogger(console=not plexpy.QUIET, log_dir=False, verbose=True) @@ -186,7 +204,7 @@ def main(): if args.datadir: plexpy.DATA_DIR = args.datadir elif plexpy.FROZEN: - plexpy.DATA_DIR = appdirs.user_data_dir("Tautulli", False) + plexpy.DATA_DIR = platformdirs.user_data_dir("Tautulli", False) else: plexpy.DATA_DIR = plexpy.PROG_DIR diff --git a/data/interfaces/default/base.html b/data/interfaces/default/base.html index 19edb94b..d6c9f859 100644 --- a/data/interfaces/default/base.html +++ b/data/interfaces/default/base.html @@ -13,6 +13,7 @@ + @@ -123,11 +124,6 @@ % else:
  • Graphs
  • % endif - % if title == "Synced Items": -
  • Synced Items
  • - % else: -
  • Synced Items
  • - % endif % if title == "Settings":
  • Patreon
  • Stripe
  • PayPal
  • -
  • Crypto
  • +
  • Crypto
  • @@ -287,7 +283,16 @@ ${next.modalIncludes()}

    - Click the button below to continue to Coinbase. + Select a cryptocurrency. +

    + +
    +
    + + +
    +

    + Or click the button below to continue to Coinbase.

    % elif stat_id == 'top_libraries': - % if row0['thumb'].startswith('http'): - + % if row0['library_thumb'].startswith('http'): + % else: % endif @@ -147,7 +148,8 @@ DOCUMENTATION :: END data-rating_key="${row.get('rating_key')}" data-grandparent_rating_key="${row.get('grandparent_rating_key')}" data-guid="${row.get('guid')}" data-title="${row.get('title')}" data-art="${row.get('art')}" data-thumb="${row.get('thumb')}" data-platform="${row.get('platform_name')}" data-library-type="${row.get('section_type')}" data-user_id="${row.get('user_id')}" data-user="${row.get('user')}" data-friendly_name="${row.get('friendly_name')}" data-user_thumb="${row.get('user_thumb')}" - data-last_watch="${row.get('last_watch')}" data-started="${row.get('started')}" data-live="${row.get('live')}" data-library_art="${row.get('library_art', '')}"> + data-last_watch="${row.get('last_watch')}" data-started="${row.get('started')}" data-live="${row.get('live')}" + data-library_art="${row.get('library_art', '')}" data-library_thumb="${row.get('library_thumb', '')}">
    ${loop.index + 1}
    % if stat_id in ('top_movies', 'popular_movies', 'top_tv', 'popular_tv', 'top_music', 'popular_music', 'last_watched'): @@ -175,7 +177,9 @@ DOCUMENTATION :: END % elif stat_id == 'top_platforms': ${row['platform']} % elif stat_id == 'most_concurrent': - ${row['title']} + + ${row['title']} + % endif
    diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index e094d0e4..ca95be42 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -92,10 +92,10 @@

    Recently Added

    @@ -212,28 +212,6 @@
    -<% from plexpy.helpers import anon_url %> - % endif
    - % if media_info['channel_identifier']: - Channel ${media_info['channel_call_sign']} ${media_info['channel_identifier']} + % if media_info['channel_vcn']: + Channel ${media_info['channel_title'] or (media_info['channel_vcn'] + ' ' + media_info['channel_call_sign'])} % endif
    @@ -692,7 +692,7 @@ DOCUMENTATION :: END Started Paused Stopped - Duration + Duration @@ -878,7 +878,7 @@ DOCUMENTATION :: END transcode_decision: transcode_decision, user_id: "${history_user_id}", % if data['live']: - guid: "${data['guid']} + guid: "${data['guid']}" % elif data['media_type'] in ('show', 'artist'): grandparent_rating_key: "${data['rating_key']}" % elif data['media_type'] in ('season', 'album'): @@ -947,8 +947,12 @@ DOCUMENTATION :: END url: 'item_watch_time_stats', async: true, data: { + % if data['live']: + guid: "${data['guid']}" + % else: rating_key: "${data['rating_key']}", - media_type: "${data['media_type']}" + media_type: "${data['media_type']}" + % endif }, complete: function(xhr, status) { $("#watch-time-stats").html(xhr.responseText); @@ -959,8 +963,12 @@ DOCUMENTATION :: END url: 'item_user_stats', async: true, data: { + % if data['live']: + guid: "${data['guid']}" + % else: rating_key: "${data['rating_key']}", - media_type: "${data['media_type']}" + media_type: "${data['media_type']}" + % endif }, complete: function(xhr, status) { $("#user-stats").html(xhr.responseText); diff --git a/data/interfaces/default/js/bootstrap-select.min.js b/data/interfaces/default/js/bootstrap-select.min.js new file mode 100644 index 00000000..92e3a32e --- /dev/null +++ b/data/interfaces/default/js/bootstrap-select.min.js @@ -0,0 +1,9 @@ +/*! + * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2020 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){!function(z){"use strict";var d=["sanitize","whiteList","sanitizeFn"],r=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],e={"*":["class","dir","id","lang","role","tabindex","style",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},l=/^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi,a=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;function v(e,t){var i=e.nodeName.toLowerCase();if(-1!==z.inArray(i,t))return-1===z.inArray(i,r)||Boolean(e.nodeValue.match(l)||e.nodeValue.match(a));for(var s=z(t).filter(function(e,t){return t instanceof RegExp}),n=0,o=s.length;n]+>/g,"")),s&&(a=w(a)),a=a.toUpperCase(),o="contains"===i?0<=a.indexOf(t):a.startsWith(t)))break}return o}function L(e){return parseInt(e,10)||0}z.fn.triggerNative=function(e){var t,i=this[0];i.dispatchEvent?(u?t=new Event(e,{bubbles:!0}):(t=document.createEvent("Event")).initEvent(e,!0,!1),i.dispatchEvent(t)):i.fireEvent?((t=document.createEventObject()).eventType=e,i.fireEvent("on"+e,t)):this.trigger(e)};var f={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"},m=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,g=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\u1ab0-\\u1aff\\u1dc0-\\u1dff]","g");function b(e){return f[e]}function w(e){return(e=e.toString())&&e.replace(m,b).replace(g,"")}var I,x,y,$,S=(I={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},x="(?:"+Object.keys(I).join("|")+")",y=RegExp(x),$=RegExp(x,"g"),function(e){return e=null==e?"":""+e,y.test(e)?e.replace($,E):e});function E(e){return I[e]}var C={32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"},N=27,D=13,H=32,W=9,B=38,M=40,R={success:!1,major:"3"};try{R.full=(z.fn.dropdown.Constructor.VERSION||"").split(" ")[0].split("."),R.major=R.full[0],R.success=!0}catch(e){}var U=0,j=".bs.select",V={DISABLED:"disabled",DIVIDER:"divider",SHOW:"open",DROPUP:"dropup",MENU:"dropdown-menu",MENURIGHT:"dropdown-menu-right",MENULEFT:"dropdown-menu-left",BUTTONCLASS:"btn-default",POPOVERHEADER:"popover-title",ICONBASE:"glyphicon",TICKICON:"glyphicon-ok"},F={MENU:"."+V.MENU},_={span:document.createElement("span"),i:document.createElement("i"),subtext:document.createElement("small"),a:document.createElement("a"),li:document.createElement("li"),whitespace:document.createTextNode("\xa0"),fragment:document.createDocumentFragment()};_.a.setAttribute("role","option"),"4"===R.major&&(_.a.className="dropdown-item"),_.subtext.className="text-muted",_.text=_.span.cloneNode(!1),_.text.className="text",_.checkMark=_.span.cloneNode(!1);var G=new RegExp(B+"|"+M),q=new RegExp("^"+W+"$|"+N),K={li:function(e,t,i){var s=_.li.cloneNode(!1);return e&&(1===e.nodeType||11===e.nodeType?s.appendChild(e):s.innerHTML=e),void 0!==t&&""!==t&&(s.className=t),null!=i&&s.classList.add("optgroup-"+i),s},a:function(e,t,i){var s=_.a.cloneNode(!0);return e&&(11===e.nodeType?s.appendChild(e):s.insertAdjacentHTML("beforeend",e)),void 0!==t&&""!==t&&s.classList.add.apply(s.classList,t.split(" ")),i&&s.setAttribute("style",i),s},text:function(e,t){var i,s,n=_.text.cloneNode(!1);if(e.content)n.innerHTML=e.content;else{if(n.textContent=e.text,e.icon){var o=_.whitespace.cloneNode(!1);(s=(!0===t?_.i:_.span).cloneNode(!1)).className=this.options.iconBase+" "+e.icon,_.fragment.appendChild(s),_.fragment.appendChild(o)}e.subtext&&((i=_.subtext.cloneNode(!1)).textContent=e.subtext,n.appendChild(i))}if(!0===t)for(;0'},maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1,windowPadding:0,virtualScroll:600,display:!1,sanitize:!0,sanitizeFn:null,whiteList:e},Y.prototype={constructor:Y,init:function(){var i=this,e=this.$element.attr("id");U++,this.selectId="bs-select-"+U,this.$element[0].classList.add("bs-select-hidden"),this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),this.$element[0].classList.contains("show-tick")&&(this.options.showTick=!0),this.$newElement=this.createDropdown(),this.buildData(),this.$element.after(this.$newElement).prependTo(this.$newElement),this.$button=this.$newElement.children("button"),this.$menu=this.$newElement.children(F.MENU),this.$menuInner=this.$menu.children(".inner"),this.$searchbox=this.$menu.find("input"),this.$element[0].classList.remove("bs-select-hidden"),!0===this.options.dropdownAlignRight&&this.$menu[0].classList.add(V.MENURIGHT),void 0!==e&&this.$button.attr("data-id",e),this.checkDisabled(),this.clickListener(),this.options.liveSearch?(this.liveSearchListener(),this.focusedParent=this.$searchbox[0]):this.focusedParent=this.$menuInner[0],this.setStyle(),this.render(),this.setWidth(),this.options.container?this.selectPosition():this.$element.on("hide"+j,function(){if(i.isVirtual()){var e=i.$menuInner[0],t=e.firstChild.cloneNode(!1);e.replaceChild(t,e.firstChild),e.scrollTop=0}}),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile(),this.$newElement.on({"hide.bs.dropdown":function(e){i.$element.trigger("hide"+j,e)},"hidden.bs.dropdown":function(e){i.$element.trigger("hidden"+j,e)},"show.bs.dropdown":function(e){i.$element.trigger("show"+j,e)},"shown.bs.dropdown":function(e){i.$element.trigger("shown"+j,e)}}),i.$element[0].hasAttribute("required")&&this.$element.on("invalid"+j,function(){i.$button[0].classList.add("bs-invalid"),i.$element.on("shown"+j+".invalid",function(){i.$element.val(i.$element.val()).off("shown"+j+".invalid")}).on("rendered"+j,function(){this.validity.valid&&i.$button[0].classList.remove("bs-invalid"),i.$element.off("rendered"+j)}),i.$button.on("blur"+j,function(){i.$element.trigger("focus").trigger("blur"),i.$button.off("blur"+j)})}),setTimeout(function(){i.buildList(),i.$element.trigger("loaded"+j)})},createDropdown:function(){var e=this.multiple||this.options.showTick?" show-tick":"",t=this.multiple?' aria-multiselectable="true"':"",i="",s=this.autofocus?" autofocus":"";R.major<4&&this.$element.parent().hasClass("input-group")&&(i=" input-group-btn");var n,o="",r="",l="",a="";return this.options.header&&(o='
    '+this.options.header+"
    "),this.options.liveSearch&&(r=''),this.multiple&&this.options.actionsBox&&(l='
    "),this.multiple&&this.options.doneButton&&(a='
    "),n='",z(n)},setPositionData:function(){this.selectpicker.view.canHighlight=[];for(var e=this.selectpicker.view.size=0;e=this.options.virtualScroll||!0===this.options.virtualScroll},createView:function(A,e,t){var L,N,D=this,i=0,H=[];if(this.selectpicker.isSearching=A,this.selectpicker.current=A?this.selectpicker.search:this.selectpicker.main,this.setPositionData(),e)if(t)i=this.$menuInner[0].scrollTop;else if(!D.multiple){var s=D.$element[0],n=(s.options[s.selectedIndex]||{}).liIndex;if("number"==typeof n&&!1!==D.options.size){var o=D.selectpicker.main.data[n],r=o&&o.position;r&&(i=r-(D.sizeInfo.menuInnerHeight+D.sizeInfo.liHeight)/2)}}function l(e,t){var i,s,n,o,r,l,a,c,d=D.selectpicker.current.elements.length,h=[],p=!0,u=D.isVirtual();D.selectpicker.view.scrollTop=e,i=Math.ceil(D.sizeInfo.menuInnerHeight/D.sizeInfo.liHeight*1.5),s=Math.round(d/i)||1;for(var f=0;fd-1?0:D.selectpicker.current.data[d-1].position-D.selectpicker.current.data[D.selectpicker.view.position1-1].position,b.firstChild.style.marginTop=v+"px",b.firstChild.style.marginBottom=g+"px"):(b.firstChild.style.marginTop=0,b.firstChild.style.marginBottom=0),b.firstChild.appendChild(w),!0===u&&D.sizeInfo.hasScrollBar){var C=b.firstChild.offsetWidth;if(t&&CD.sizeInfo.selectWidth)b.firstChild.style.minWidth=D.sizeInfo.menuInnerInnerWidth+"px";else if(C>D.sizeInfo.menuInnerInnerWidth){D.$menu[0].style.minWidth=0;var O=b.firstChild.offsetWidth;O>D.sizeInfo.menuInnerInnerWidth&&(D.sizeInfo.menuInnerInnerWidth=O,b.firstChild.style.minWidth=D.sizeInfo.menuInnerInnerWidth+"px"),D.$menu[0].style.minWidth=""}}}if(D.prevActiveIndex=D.activeIndex,D.options.liveSearch){if(A&&t){var z,T=0;D.selectpicker.view.canHighlight[T]||(T=1+D.selectpicker.view.canHighlight.slice(1).indexOf(!0)),z=D.selectpicker.view.visibleElements[T],D.defocusItem(D.selectpicker.view.currentActive),D.activeIndex=(D.selectpicker.current.data[T]||{}).index,D.focusItem(z)}}else D.$menuInner.trigger("focus")}l(i,!0),this.$menuInner.off("scroll.createView").on("scroll.createView",function(e,t){D.noScroll||l(this.scrollTop,t),D.noScroll=!1}),z(window).off("resize"+j+"."+this.selectId+".createView").on("resize"+j+"."+this.selectId+".createView",function(){D.$newElement.hasClass(V.SHOW)&&l(D.$menuInner[0].scrollTop)})},focusItem:function(e,t,i){if(e){t=t||this.selectpicker.main.data[this.activeIndex];var s=e.firstChild;s&&(s.setAttribute("aria-setsize",this.selectpicker.view.size),s.setAttribute("aria-posinset",t.posinset),!0!==i&&(this.focusedParent.setAttribute("aria-activedescendant",s.id),e.classList.add("active"),s.classList.add("active")))}},defocusItem:function(e){e&&(e.classList.remove("active"),e.firstChild&&e.firstChild.classList.remove("active"))},setPlaceholder:function(){var e=!1;if(this.options.title&&!this.multiple){this.selectpicker.view.titleOption||(this.selectpicker.view.titleOption=document.createElement("option")),e=!0;var t=this.$element[0],i=!1,s=!this.selectpicker.view.titleOption.parentNode;if(s)this.selectpicker.view.titleOption.className="bs-title-option",this.selectpicker.view.titleOption.value="",i=void 0===z(t.options[t.selectedIndex]).attr("selected")&&void 0===this.$element.data("selected");!s&&0===this.selectpicker.view.titleOption.index||t.insertBefore(this.selectpicker.view.titleOption,t.firstChild),i&&(t.selectedIndex=0)}return e},buildData:function(){var p=':not([hidden]):not([data-hidden="true"])',u=[],f=0,e=this.setPlaceholder()?1:0;this.options.hideDisabled&&(p+=":not(:disabled)");var t=this.$element[0].querySelectorAll("select > *"+p);function m(e){var t=u[u.length-1];t&&"divider"===t.type&&(t.optID||e.optID)||((e=e||{}).type="divider",u.push(e))}function v(e,t){if((t=t||{}).divider="true"===e.getAttribute("data-divider"),t.divider)m({optID:t.optID});else{var i=u.length,s=e.style.cssText,n=s?S(s):"",o=(e.className||"")+(t.optgroupClass||"");t.optID&&(o="opt "+o),t.optionClass=o.trim(),t.inlineStyle=n,t.text=e.textContent,t.content=e.getAttribute("data-content"),t.tokens=e.getAttribute("data-tokens"),t.subtext=e.getAttribute("data-subtext"),t.icon=e.getAttribute("data-icon"),e.liIndex=i,t.display=t.content||t.text,t.type="option",t.index=i,t.option=e,t.selected=!!e.selected,t.disabled=t.disabled||!!e.disabled,u.push(t)}}function i(e,t){var i=t[e],s=t[e-1],n=t[e+1],o=i.querySelectorAll("option"+p);if(o.length){var r,l,a={display:S(i.label),subtext:i.getAttribute("data-subtext"),icon:i.getAttribute("data-icon"),type:"optgroup-label",optgroupClass:" "+(i.className||"")};f++,s&&m({optID:f}),a.optID=f,u.push(a);for(var c=0,d=o.length;c li")},render:function(){var e,t=this,i=this.$element[0],s=this.setPlaceholder()&&0===i.selectedIndex,n=O(i,this.options.hideDisabled),o=n.length,r=this.$button[0],l=r.querySelector(".filter-option-inner-inner"),a=document.createTextNode(this.options.multipleSeparator),c=_.fragment.cloneNode(!1),d=!1;if(r.classList.toggle("bs-placeholder",t.multiple?!o:!T(i,n)),this.tabIndex(),"static"===this.options.selectedTextFormat)c=K.text.call(this,{text:this.options.title},!0);else if(!1===(this.multiple&&-1!==this.options.selectedTextFormat.indexOf("count")&&1")).length&&o>e[1]||1===e.length&&2<=o))){if(!s){for(var h=0;h option"+m+", optgroup"+m+" option"+m).length,g="function"==typeof this.options.countSelectedText?this.options.countSelectedText(o,v):this.options.countSelectedText;c=K.text.call(this,{text:g.replace("{0}",o.toString()).replace("{1}",v.toString())},!0)}if(null==this.options.title&&(this.options.title=this.$element.attr("title")),c.childNodes.length||(c=K.text.call(this,{text:void 0!==this.options.title?this.options.title:this.options.noneSelectedText},!0)),r.title=c.textContent.replace(/<[^>]*>?/g,"").trim(),this.options.sanitize&&d&&P([c],t.options.whiteList,t.options.sanitizeFn),l.innerHTML="",l.appendChild(c),R.major<4&&this.$newElement[0].classList.contains("bs3-has-addon")){var b=r.querySelector(".filter-expand"),w=l.cloneNode(!0);w.className="filter-expand",b?r.replaceChild(w,b):r.appendChild(w)}this.$element.trigger("rendered"+j)},setStyle:function(e,t){var i,s=this.$button[0],n=this.$newElement[0],o=this.options.style.trim();this.$element.attr("class")&&this.$newElement.addClass(this.$element.attr("class").replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi,"")),R.major<4&&(n.classList.add("bs3"),n.parentNode.classList.contains("input-group")&&(n.previousElementSibling||n.nextElementSibling)&&(n.previousElementSibling||n.nextElementSibling).classList.contains("input-group-addon")&&n.classList.add("bs3-has-addon")),i=e?e.trim():o,"add"==t?i&&s.classList.add.apply(s.classList,i.split(" ")):"remove"==t?i&&s.classList.remove.apply(s.classList,i.split(" ")):(o&&s.classList.remove.apply(s.classList,o.split(" ")),i&&s.classList.add.apply(s.classList,i.split(" ")))},liHeight:function(e){if(e||!1!==this.options.size&&!Object.keys(this.sizeInfo).length){var t=document.createElement("div"),i=document.createElement("div"),s=document.createElement("div"),n=document.createElement("ul"),o=document.createElement("li"),r=document.createElement("li"),l=document.createElement("li"),a=document.createElement("a"),c=document.createElement("span"),d=this.options.header&&0this.sizeInfo.menuExtras.vert&&l+this.sizeInfo.menuExtras.vert+50>this.sizeInfo.selectOffsetBot,!0===this.selectpicker.isSearching&&(a=this.selectpicker.dropup),this.$newElement.toggleClass(V.DROPUP,a),this.selectpicker.dropup=a),"auto"===this.options.size)n=3this.options.size){for(var b=0;bthis.sizeInfo.menuInnerHeight&&(this.sizeInfo.hasScrollBar=!0,this.sizeInfo.totalMenuWidth=this.sizeInfo.menuWidth+this.sizeInfo.scrollBarWidth),"auto"===this.options.dropdownAlignRight&&this.$menu.toggleClass(V.MENURIGHT,this.sizeInfo.selectOffsetLeft>this.sizeInfo.selectOffsetRight&&this.sizeInfo.selectOffsetRightthis.options.size&&i.off("resize"+j+"."+this.selectId+".setMenuSize scroll"+j+"."+this.selectId+".setMenuSize")}this.createView(!1,!0,e)},setWidth:function(){var i=this;"auto"===this.options.width?requestAnimationFrame(function(){i.$menu.css("min-width","0"),i.$element.on("loaded"+j,function(){i.liHeight(),i.setMenuSize();var e=i.$newElement.clone().appendTo("body"),t=e.css("width","auto").children("button").outerWidth();e.remove(),i.sizeInfo.selectWidth=Math.max(i.sizeInfo.totalMenuWidth,t),i.$newElement.css("width",i.sizeInfo.selectWidth+"px")})}):"fit"===this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width","").addClass("fit-width")):this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width",this.options.width)):(this.$menu.css("min-width",""),this.$newElement.css("width","")),this.$newElement.hasClass("fit-width")&&"fit"!==this.options.width&&this.$newElement[0].classList.remove("fit-width")},selectPosition:function(){this.$bsContainer=z('
    ');function e(e){var t={},i=r.options.display||!!z.fn.dropdown.Constructor.Default&&z.fn.dropdown.Constructor.Default.display;r.$bsContainer.addClass(e.attr("class").replace(/form-control|fit-width/gi,"")).toggleClass(V.DROPUP,e.hasClass(V.DROPUP)),s=e.offset(),l.is("body")?n={top:0,left:0}:((n=l.offset()).top+=parseInt(l.css("borderTopWidth"))-l.scrollTop(),n.left+=parseInt(l.css("borderLeftWidth"))-l.scrollLeft()),o=e.hasClass(V.DROPUP)?0:e[0].offsetHeight,(R.major<4||"static"===i)&&(t.top=s.top-n.top+o,t.left=s.left-n.left),t.width=e[0].offsetWidth,r.$bsContainer.css(t)}var s,n,o,r=this,l=z(this.options.container);this.$button.on("click.bs.dropdown.data-api",function(){r.isDisabled()||(e(r.$newElement),r.$bsContainer.appendTo(r.options.container).toggleClass(V.SHOW,!r.$button.hasClass(V.SHOW)).append(r.$menu))}),z(window).off("resize"+j+"."+this.selectId+" scroll"+j+"."+this.selectId).on("resize"+j+"."+this.selectId+" scroll"+j+"."+this.selectId,function(){r.$newElement.hasClass(V.SHOW)&&e(r.$newElement)}),this.$element.on("hide"+j,function(){r.$menu.data("height",r.$menu.height()),r.$bsContainer.detach()})},setOptionStatus:function(e){var t=this;if(t.noScroll=!1,t.selectpicker.view.visibleElements&&t.selectpicker.view.visibleElements.length)for(var i=0;i
    ');y[2]&&($=$.replace("{var}",y[2][1"+$+"
    ")),d=!1,C.$element.trigger("maxReached"+j)),g&&w&&(E.append(z("
    "+S+"
    ")),d=!1,C.$element.trigger("maxReachedGrp"+j)),setTimeout(function(){C.setSelected(r,!1)},10),E[0].classList.add("fadeOut"),setTimeout(function(){E.remove()},1050)}}}else c&&(c.selected=!1),h.selected=!0,C.setSelected(r,!0);!C.multiple||C.multiple&&1===C.options.maxOptions?C.$button.trigger("focus"):C.options.liveSearch&&C.$searchbox.trigger("focus"),d&&(!C.multiple&&a===s.selectedIndex||(A=[h.index,p.prop("selected"),l],C.$element.triggerNative("change")))}}),this.$menu.on("click","li."+V.DISABLED+" a, ."+V.POPOVERHEADER+", ."+V.POPOVERHEADER+" :not(.close)",function(e){e.currentTarget==this&&(e.preventDefault(),e.stopPropagation(),C.options.liveSearch&&!z(e.target).hasClass("close")?C.$searchbox.trigger("focus"):C.$button.trigger("focus"))}),this.$menuInner.on("click",".divider, .dropdown-header",function(e){e.preventDefault(),e.stopPropagation(),C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus")}),this.$menu.on("click","."+V.POPOVERHEADER+" .close",function(){C.$button.trigger("click")}),this.$searchbox.on("click",function(e){e.stopPropagation()}),this.$menu.on("click",".actions-btn",function(e){C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus"),e.preventDefault(),e.stopPropagation(),z(this).hasClass("bs-select-all")?C.selectAll():C.deselectAll()}),this.$element.on("change"+j,function(){C.render(),C.$element.trigger("changed"+j,A),A=null}).on("focus"+j,function(){C.options.mobile||C.$button.trigger("focus")})},liveSearchListener:function(){var u=this,f=document.createElement("li");this.$button.on("click.bs.dropdown.data-api",function(){u.$searchbox.val()&&u.$searchbox.val("")}),this.$searchbox.on("click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api",function(e){e.stopPropagation()}),this.$searchbox.on("input propertychange",function(){var e=u.$searchbox.val();if(u.selectpicker.search.elements=[],u.selectpicker.search.data=[],e){var t=[],i=e.toUpperCase(),s={},n=[],o=u._searchStyle(),r=u.options.liveSearchNormalize;r&&(i=w(i));for(var l=0;l=a.selectpicker.view.canHighlight.length&&(t=0),a.selectpicker.view.canHighlight[t+f]||(t=t+1+a.selectpicker.view.canHighlight.slice(t+f+1).indexOf(!0))),e.preventDefault();var m=f+t;e.which===B?0===f&&t===c.length-1?(a.$menuInner[0].scrollTop=a.$menuInner[0].scrollHeight,m=a.selectpicker.current.elements.length-1):d=(o=(n=a.selectpicker.current.data[m]).position-n.height)u+a.sizeInfo.menuInnerHeight),s=a.selectpicker.main.elements[v],a.activeIndex=b[x],a.focusItem(s),s&&s.firstChild.focus(),d&&(a.$menuInner[0].scrollTop=o),r.trigger("focus")}}i&&(e.which===H&&!a.selectpicker.keydown.keyHistory||e.which===D||e.which===W&&a.options.selectOnTab)&&(e.which!==H&&e.preventDefault(),a.options.liveSearch&&e.which===H||(a.$menuInner.find(".active a").trigger("click",!0),r.trigger("focus"),a.options.liveSearch||(e.preventDefault(),z(document).data("spaceSelect",!0))))}},mobile:function(){this.$element[0].classList.add("mobile-device")},refresh:function(){var e=z.extend({},this.options,this.$element.data());this.options=e,this.checkDisabled(),this.setStyle(),this.render(),this.buildData(),this.buildList(),this.setWidth(),this.setSize(!0),this.$element.trigger("refreshed"+j)},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),this.$bsContainer?this.$bsContainer.remove():this.$menu.remove(),this.$element.off(j).removeData("selectpicker").removeClass("bs-select-hidden selectpicker"),z(window).off(j+"."+this.selectId)}};var J=z.fn.selectpicker;z.fn.selectpicker=Z,z.fn.selectpicker.Constructor=Y,z.fn.selectpicker.noConflict=function(){return z.fn.selectpicker=J,this};var Q=z.fn.dropdown.Constructor._dataApiKeydownHandler||z.fn.dropdown.Constructor.prototype.keydown;z(document).off("keydown.bs.dropdown.data-api").on("keydown.bs.dropdown.data-api",':not(.bootstrap-select) > [data-toggle="dropdown"]',Q).on("keydown.bs.dropdown.data-api",":not(.bootstrap-select) > .dropdown-menu",Q).on("keydown"+j,'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',Y.prototype.keydown).on("focusin.modal",'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',function(e){e.stopPropagation()}),z(window).on("load"+j+".data-api",function(){z(".selectpicker").each(function(){var e=z(this);Z.call(e,e.data())})})}(e)}); +//# sourceMappingURL=bootstrap-select.min.js.map \ No newline at end of file diff --git a/data/interfaces/default/js/graphs/concurrent_streams_by_stream_type.js b/data/interfaces/default/js/graphs/concurrent_streams_by_stream_type.js new file mode 100644 index 00000000..623e4735 --- /dev/null +++ b/data/interfaces/default/js/graphs/concurrent_streams_by_stream_type.js @@ -0,0 +1,76 @@ +var formatter_function = function() { + if (moment(this.x, 'X').isValid() && (this.x > 946684800)) { + var s = ''+ moment(this.x).format('ddd MMM D') +''; + } else { + var s = ''+ this.x +''; + } + $.each(this.points, function(i, point) { + s += '
    '+point.series.name+': '+point.y; + }); + return s; +}; + +var hc_concurrent_streams_by_stream_type_options = { + chart: { + type: 'line', + backgroundColor: 'rgba(0,0,0,0)', + renderTo: 'graph_concurrent_streams_by_stream_type' + }, + title: { + text: '' + }, + legend: { + enabled: true, + itemStyle: { + font: '9pt "Open Sans", sans-serif', + color: '#A0A0A0' + }, + itemHoverStyle: { + color: '#FFF' + }, + itemHiddenStyle: { + color: '#444' + } + }, + credits: { + enabled: false + }, + plotOptions: { + series: { + events: { + legendItemClick: function() { + setGraphVisibility(this.chart.renderTo.id, this.chart.series, this.name); + } + } + } + }, + xAxis: { + type: 'datetime', + labels: { + formatter: function() { + return moment(this.value).format("MMM D"); + }, + style: { + color: '#aaa' + } + }, + categories: [{}], + plotBands: [] + }, + yAxis: { + title: { + text: null + }, + labels: { + style: { + color: '#aaa' + } + } + }, + tooltip: { + shared: true, + crosshairs: true, + formatter: formatter_function + }, + series: [{}] +}; \ No newline at end of file diff --git a/data/interfaces/default/js/jquery.scrollbar.min.js b/data/interfaces/default/js/jquery.scrollbar.min.js index 5a86f9ad..cf9d8935 100644 --- a/data/interfaces/default/js/jquery.scrollbar.min.js +++ b/data/interfaces/default/js/jquery.scrollbar.min.js @@ -13,4 +13,4 @@ * @url https://github.com/gromo/jquery.scrollbar/ * */ -!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):b("undefined"!=typeof exports?require("jquery"):a.jQuery)}(this,function(a){"use strict";function h(b){if(c.webkit&&!b)return{height:0,width:0};if(!c.data.outer){var d={border:"none","box-sizing":"content-box",height:"200px",margin:"0",padding:"0",width:"200px"};c.data.inner=a("
    ").css(a.extend({},d)),c.data.outer=a("
    ").css(a.extend({left:"-1000px",overflow:"scroll",position:"absolute",top:"-1000px"},d)).append(c.data.inner).appendTo("body")}return c.data.outer.scrollLeft(1e3).scrollTop(1e3),{height:Math.ceil(c.data.outer.offset().top-c.data.inner.offset().top||0),width:Math.ceil(c.data.outer.offset().left-c.data.inner.offset().left||0)}}function i(){var a=h(!0);return!(a.height||a.width)}function j(a){var b=a.originalEvent;return(!b.axis||b.axis!==b.HORIZONTAL_AXIS)&&!b.wheelDeltaX}var b=!1,c={data:{index:0,name:"scrollbar"},firefox:/firefox/i.test(navigator.userAgent),macosx:/mac/i.test(navigator.platform),msedge:/edge\/\d+/i.test(navigator.userAgent),msie:/(msie|trident)/i.test(navigator.userAgent),mobile:/android|webos|iphone|ipad|ipod|blackberry/i.test(navigator.userAgent),overlay:null,scroll:null,scrolls:[],webkit:/webkit/i.test(navigator.userAgent)&&!/edge\/\d+/i.test(navigator.userAgent)};c.scrolls.add=function(a){this.remove(a).push(a)},c.scrolls.remove=function(b){for(;a.inArray(b,this)>=0;)this.splice(a.inArray(b,this),1);return this};var d={autoScrollSize:!0,autoUpdate:!0,debug:!1,disableBodyScroll:!1,duration:200,ignoreMobile:!1,ignoreOverlay:!1,isRtl:!1,scrollStep:30,showArrows:!1,stepScrolling:!0,scrollx:null,scrolly:null,onDestroy:null,onFallback:null,onInit:null,onScroll:null,onUpdate:null},e=function(b){c.scroll||(c.overlay=i(),c.scroll=h(),g(),a(window).resize(function(){var a=!1;if(c.scroll&&(c.scroll.height||c.scroll.width)){var b=h();b.height===c.scroll.height&&b.width===c.scroll.width||(c.scroll=b,a=!0)}g(a)})),this.container=b,this.namespace=".scrollbar_"+c.data.index++,this.options=a.extend({},d,window.jQueryScrollbarOptions||{}),this.scrollTo=null,this.scrollx={},this.scrolly={},b.data(c.data.name,this),c.scrolls.add(this)};e.prototype={destroy:function(){if(this.wrapper){this.container.removeData(c.data.name),c.scrolls.remove(this);var b=this.container.scrollLeft(),d=this.container.scrollTop();this.container.insertBefore(this.wrapper).css({height:"",margin:"","max-height":""}).removeClass("scroll-content scroll-scrollx_visible scroll-scrolly_visible").off(this.namespace).scrollLeft(b).scrollTop(d),this.scrollx.scroll.removeClass("scroll-scrollx_visible").find("div").addBack().off(this.namespace),this.scrolly.scroll.removeClass("scroll-scrolly_visible").find("div").addBack().off(this.namespace),this.wrapper.remove(),a(document).add("body").off(this.namespace),a.isFunction(this.options.onDestroy)&&this.options.onDestroy.apply(this,[this.container])}},init:function(b){var d=this,e=this.container,f=this.containerWrapper||e,g=this.namespace,h=a.extend(this.options,b||{}),i={x:this.scrollx,y:this.scrolly},k=this.wrapper,l={},m={scrollLeft:e.scrollLeft(),scrollTop:e.scrollTop()};if(c.mobile&&h.ignoreMobile||c.overlay&&h.ignoreOverlay||c.macosx&&!c.webkit)return a.isFunction(h.onFallback)&&h.onFallback.apply(this,[e]),!1;if(k)l={height:"auto","margin-bottom":c.scroll.height*-1+"px","max-height":""},l[h.isRtl?"margin-left":"margin-right"]=c.scroll.width*-1+"px",f.css(l);else{if(this.wrapper=k=a("
    ").addClass("scroll-wrapper").addClass(e.attr("class")).css("position","absolute"===e.css("position")?"absolute":"relative").insertBefore(e).append(e),h.isRtl&&k.addClass("scroll--rtl"),e.is("textarea")&&(this.containerWrapper=f=a("
    ").insertBefore(e).append(e),k.addClass("scroll-textarea")),l={height:"auto","margin-bottom":c.scroll.height*-1+"px","max-height":""},l[h.isRtl?"margin-left":"margin-right"]=c.scroll.width*-1+"px",f.addClass("scroll-content").css(l),e.on("scroll"+g,function(b){var f=e.scrollLeft(),g=e.scrollTop();if(h.isRtl)switch(!0){case c.firefox:f=Math.abs(f);case c.msedge||c.msie:f=e[0].scrollWidth-e[0].clientWidth-f}a.isFunction(h.onScroll)&&h.onScroll.call(d,{maxScroll:i.y.maxScrollOffset,scroll:g,size:i.y.size,visible:i.y.visible},{maxScroll:i.x.maxScrollOffset,scroll:f,size:i.x.size,visible:i.x.visible}),i.x.isVisible&&i.x.scroll.bar.css("left",f*i.x.kx+"px"),i.y.isVisible&&i.y.scroll.bar.css("top",g*i.y.kx+"px")}),k.on("scroll"+g,function(){k.scrollTop(0).scrollLeft(0)}),h.disableBodyScroll){var n=function(a){j(a)?i.y.isVisible&&i.y.mousewheel(a):i.x.isVisible&&i.x.mousewheel(a)};k.on("MozMousePixelScroll"+g,n),k.on("mousewheel"+g,n),c.mobile&&k.on("touchstart"+g,function(b){var c=b.originalEvent.touches&&b.originalEvent.touches[0]||b,d={pageX:c.pageX,pageY:c.pageY},f={left:e.scrollLeft(),top:e.scrollTop()};a(document).on("touchmove"+g,function(a){var b=a.originalEvent.targetTouches&&a.originalEvent.targetTouches[0]||a;e.scrollLeft(f.left+d.pageX-b.pageX),e.scrollTop(f.top+d.pageY-b.pageY),a.preventDefault()}),a(document).on("touchend"+g,function(){a(document).off(g)})})}a.isFunction(h.onInit)&&h.onInit.apply(this,[e])}a.each(i,function(b,f){var k=null,l=1,m="x"===b?"scrollLeft":"scrollTop",n=h.scrollStep,o=function(){var a=e[m]();e[m](a+n),1==l&&a+n>=p&&(a=e[m]()),l==-1&&a+n<=p&&(a=e[m]()),e[m]()==a&&k&&k()},p=0;f.scroll||(f.scroll=d._getScroll(h["scroll"+b]).addClass("scroll-"+b),h.showArrows&&f.scroll.addClass("scroll-element_arrows_visible"),f.mousewheel=function(a){if(!f.isVisible||"x"===b&&j(a))return!0;if("y"===b&&!j(a))return i.x.mousewheel(a),!0;var c=a.originalEvent.wheelDelta*-1||a.originalEvent.detail,g=f.size-f.visible-f.offset;return c||("x"===b&&a.originalEvent.deltaX?c=40*a.originalEvent.deltaX:"y"===b&&a.originalEvent.deltaY&&(c=40*a.originalEvent.deltaY)),(c>0&&p0)&&(p+=c,p<0&&(p=0),p>g&&(p=g),d.scrollTo=d.scrollTo||{},d.scrollTo[m]=p,setTimeout(function(){d.scrollTo&&(e.stop().animate(d.scrollTo,240,"linear",function(){p=e[m]()}),d.scrollTo=null)},1)),a.preventDefault(),!1},f.scroll.on("MozMousePixelScroll"+g,f.mousewheel).on("mousewheel"+g,f.mousewheel).on("mouseenter"+g,function(){p=e[m]()}),f.scroll.find(".scroll-arrow, .scroll-element_track").on("mousedown"+g,function(g){if(1!=g.which)return!0;l=1;var i={eventOffset:g["x"===b?"pageX":"pageY"],maxScrollValue:f.size-f.visible-f.offset,scrollbarOffset:f.scroll.bar.offset()["x"===b?"left":"top"],scrollbarSize:f.scroll.bar["x"===b?"outerWidth":"outerHeight"]()},j=0,q=0;if(a(this).hasClass("scroll-arrow")){if(l=a(this).hasClass("scroll-arrow_more")?1:-1,n=h.scrollStep*l,p=l>0?i.maxScrollValue:0,h.isRtl)switch(!0){case c.firefox:p=l>0?0:i.maxScrollValue*-1;break;case c.msie||c.msedge:}}else l=i.eventOffset>i.scrollbarOffset+i.scrollbarSize?1:i.eventOffset','
    ','
    ','
    ','
    ','
    ','
    ','
    ','
    ',"
    ","
    ",'
    ','
    ','
    ',"
    ",'
    ','
    ',"
    ","
    ","
    "].join(""),simple:['
    ','
    ','
    ','
    ','
    ',"
    ","
    "].join("")};return c[b]&&(b=c[b]),b||(b=c.simple),b="string"==typeof b?a(b).appendTo(this.wrapper):a(b),a.extend(b,{bar:b.find(".scroll-bar"),size:b.find(".scroll-element_size"),track:b.find(".scroll-element_track")}),b},_handleMouseDown:function(b,c){var d=this.namespace;return a(document).on("blur"+d,function(){a(document).add("body").off(d),b&&b()}),a(document).on("dragstart"+d,function(a){return a.preventDefault(),!1}),a(document).on("mouseup"+d,function(){a(document).add("body").off(d),b&&b()}),a("body").on("selectstart"+d,function(a){return a.preventDefault(),!1}),c&&c.preventDefault(),!1},_updateScroll:function(b,d){var e=this.container,f=this.containerWrapper||e,g="scroll-scroll"+b+"_visible",h="x"===b?this.scrolly:this.scrollx,i=parseInt(this.container.css("x"===b?"left":"top"),10)||0,j=this.wrapper,k=d.size,l=d.visible+i;d.isVisible=k-l>1,d.isVisible?(d.scroll.addClass(g),h.scroll.addClass(g),f.addClass(g)):(d.scroll.removeClass(g),h.scroll.removeClass(g),f.removeClass(g)),"y"===b&&(e.is("textarea")||k10?(window.console&&console.log("Scroll updates exceed 10"),g=function(){}):(clearTimeout(a),a=setTimeout(g,300))}}();window.angular&&!function(a){a.module("jQueryScrollbar",[]).provider("jQueryScrollbar",function(){var b=d;return{setOptions:function(c){a.extend(b,c)},$get:function(){return{options:a.copy(b)}}}}).directive("jqueryScrollbar",["jQueryScrollbar","$parse",function(a,b){return{restrict:"AC",link:function(c,d,e){var f=b(e.jqueryScrollbar),g=f(c);d.scrollbar(g||a.options).on("$destroy",function(){d.scrollbar("destroy")})}}}])}(window.angular)}); \ No newline at end of file +!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):b("undefined"!=typeof exports?require("jquery"):a.jQuery)}(this,function(a){"use strict";function h(b){if(c.webkit&&!b)return{height:0,width:0};if(!c.data.outer){var d={border:"none","box-sizing":"content-box",height:"200px",margin:"0",padding:"0",width:"200px"};c.data.inner=a("
    ").css(a.extend({},d)),c.data.outer=a("
    ").css(a.extend({left:"-1000px",overflow:"scroll",position:"absolute",top:"-1000px"},d)).append(c.data.inner).appendTo("body")}return c.data.outer.scrollLeft(1e3).scrollTop(1e3),{height:Math.ceil(c.data.outer.offset().top-c.data.inner.offset().top||0),width:Math.ceil(c.data.outer.offset().left-c.data.inner.offset().left||0)}}function i(){var a=h(!0);return!(a.height||a.width)}function j(a){var b=a.originalEvent;return(!b.axis||b.axis!==b.HORIZONTAL_AXIS)&&!b.wheelDeltaX}var b=!1,c={data:{index:0,name:"scrollbar"},firefox:/firefox/i.test(navigator.userAgent),macosx:/mac/i.test(navigator.platform),msedge:/edge\/\d+/i.test(navigator.userAgent),msie:/(msie|trident)/i.test(navigator.userAgent),mobile:/android|webos|iphone|ipad|ipod|blackberry/i.test(navigator.userAgent),overlay:null,scroll:null,scrolls:[],webkit:/webkit/i.test(navigator.userAgent)&&!/edge\/\d+/i.test(navigator.userAgent)};c.scrolls.add=function(a){this.remove(a).push(a)},c.scrolls.remove=function(b){for(;a.inArray(b,this)>=0;)this.splice(a.inArray(b,this),1);return this};var d={autoScrollSize:!0,autoUpdate:!0,debug:!1,disableBodyScroll:!1,duration:200,ignoreMobile:!1,ignoreOverlay:!1,isRtl:!1,scrollStep:30,showArrows:!1,stepScrolling:!0,scrollx:null,scrolly:null,onDestroy:null,onFallback:null,onInit:null,onScroll:null,onUpdate:null},e=function(b){c.scroll||(c.overlay=i(),c.scroll=h(),g(),a(window).resize(function(){var a=!1;if(c.scroll&&(c.scroll.height||c.scroll.width)){var b=h();b.height===c.scroll.height&&b.width===c.scroll.width||(c.scroll=b,a=!0)}g(a)})),this.container=b,this.namespace=".scrollbar_"+c.data.index++,this.options=a.extend({},d,window.jQueryScrollbarOptions||{}),this.scrollTo=null,this.scrollx={},this.scrolly={},b.data(c.data.name,this),c.scrolls.add(this)};e.prototype={destroy:function(){if(this.wrapper){this.container.removeData(c.data.name),c.scrolls.remove(this);var b=this.container.scrollLeft(),d=this.container.scrollTop();this.container.insertBefore(this.wrapper).css({height:"",margin:"","max-height":""}).removeClass("scroll-content scroll-scrollx_visible scroll-scrolly_visible").off(this.namespace).scrollLeft(b).scrollTop(d),this.scrollx.scroll.removeClass("scroll-scrollx_visible").find("div").addBack().off(this.namespace),this.scrolly.scroll.removeClass("scroll-scrolly_visible").find("div").addBack().off(this.namespace),this.wrapper.remove(),a(document).add("body").off(this.namespace),a.isFunction(this.options.onDestroy)&&this.options.onDestroy.apply(this,[this.container])}},init:function(b){var d=this,e=this.container,f=this.containerWrapper||e,g=this.namespace,h=a.extend(this.options,b||{}),i={x:this.scrollx,y:this.scrolly},k=this.wrapper,l={},m={scrollLeft:e.scrollLeft(),scrollTop:e.scrollTop()};if(c.mobile&&h.ignoreMobile||c.overlay&&h.ignoreOverlay)return a.isFunction(h.onFallback)&&h.onFallback.apply(this,[e]),!1;if(k)l={height:"auto","margin-bottom":c.scroll.height*-1+"px","max-height":""},l[h.isRtl?"margin-left":"margin-right"]=c.scroll.width*-1+"px",f.css(l);else{if(this.wrapper=k=a("
    ").addClass("scroll-wrapper").addClass(e.attr("class")).css("position","absolute"===e.css("position")?"absolute":"relative").insertBefore(e).append(e),h.isRtl&&k.addClass("scroll--rtl"),e.is("textarea")&&(this.containerWrapper=f=a("
    ").insertBefore(e).append(e),k.addClass("scroll-textarea")),l={height:"auto","margin-bottom":c.scroll.height*-1+"px","max-height":""},l[h.isRtl?"margin-left":"margin-right"]=c.scroll.width*-1+"px",f.addClass("scroll-content").css(l),e.on("scroll"+g,function(b){var f=e.scrollLeft(),g=e.scrollTop();if(h.isRtl)switch(!0){case c.firefox:f=Math.abs(f);case c.msedge||c.msie:f=e[0].scrollWidth-e[0].clientWidth-f}a.isFunction(h.onScroll)&&h.onScroll.call(d,{maxScroll:i.y.maxScrollOffset,scroll:g,size:i.y.size,visible:i.y.visible},{maxScroll:i.x.maxScrollOffset,scroll:f,size:i.x.size,visible:i.x.visible}),i.x.isVisible&&i.x.scroll.bar.css("left",f*i.x.kx+"px"),i.y.isVisible&&i.y.scroll.bar.css("top",g*i.y.kx+"px")}),k.on("scroll"+g,function(){k.scrollTop(0).scrollLeft(0)}),h.disableBodyScroll){var n=function(a){j(a)?i.y.isVisible&&i.y.mousewheel(a):i.x.isVisible&&i.x.mousewheel(a)};k.on("MozMousePixelScroll"+g,n),k.on("mousewheel"+g,n),c.mobile&&k.on("touchstart"+g,function(b){var c=b.originalEvent.touches&&b.originalEvent.touches[0]||b,d={pageX:c.pageX,pageY:c.pageY},f={left:e.scrollLeft(),top:e.scrollTop()};a(document).on("touchmove"+g,function(a){var b=a.originalEvent.targetTouches&&a.originalEvent.targetTouches[0]||a;e.scrollLeft(f.left+d.pageX-b.pageX),e.scrollTop(f.top+d.pageY-b.pageY),a.preventDefault()}),a(document).on("touchend"+g,function(){a(document).off(g)})})}a.isFunction(h.onInit)&&h.onInit.apply(this,[e])}a.each(i,function(b,f){var k=null,l=1,m="x"===b?"scrollLeft":"scrollTop",n=h.scrollStep,o=function(){var a=e[m]();e[m](a+n),1==l&&a+n>=p&&(a=e[m]()),l==-1&&a+n<=p&&(a=e[m]()),e[m]()==a&&k&&k()},p=0;f.scroll||(f.scroll=d._getScroll(h["scroll"+b]).addClass("scroll-"+b),h.showArrows&&f.scroll.addClass("scroll-element_arrows_visible"),f.mousewheel=function(a){if(!f.isVisible||"x"===b&&j(a))return!0;if("y"===b&&!j(a))return i.x.mousewheel(a),!0;var c=a.originalEvent.wheelDelta*-1||a.originalEvent.detail,g=f.size-f.visible-f.offset;return c||("x"===b&&a.originalEvent.deltaX?c=40*a.originalEvent.deltaX:"y"===b&&a.originalEvent.deltaY&&(c=40*a.originalEvent.deltaY)),(c>0&&p0)&&(p+=c,p<0&&(p=0),p>g&&(p=g),d.scrollTo=d.scrollTo||{},d.scrollTo[m]=p,setTimeout(function(){d.scrollTo&&(e.stop().animate(d.scrollTo,240,"linear",function(){p=e[m]()}),d.scrollTo=null)},1)),a.preventDefault(),!1},f.scroll.on("MozMousePixelScroll"+g,f.mousewheel).on("mousewheel"+g,f.mousewheel).on("mouseenter"+g,function(){p=e[m]()}),f.scroll.find(".scroll-arrow, .scroll-element_track").on("mousedown"+g,function(g){if(1!=g.which)return!0;l=1;var i={eventOffset:g["x"===b?"pageX":"pageY"],maxScrollValue:f.size-f.visible-f.offset,scrollbarOffset:f.scroll.bar.offset()["x"===b?"left":"top"],scrollbarSize:f.scroll.bar["x"===b?"outerWidth":"outerHeight"]()},j=0,q=0;if(a(this).hasClass("scroll-arrow")){if(l=a(this).hasClass("scroll-arrow_more")?1:-1,n=h.scrollStep*l,p=l>0?i.maxScrollValue:0,h.isRtl)switch(!0){case c.firefox:p=l>0?0:i.maxScrollValue*-1;break;case c.msie||c.msedge:}}else l=i.eventOffset>i.scrollbarOffset+i.scrollbarSize?1:i.eventOffset','
    ','
    ','
    ','
    ','
    ','
    ','
    ','
    ',"
    ","
    ",'
    ','
    ','
    ',"
    ",'
    ','
    ',"
    ","
    ","
    "].join(""),simple:['
    ','
    ','
    ','
    ','
    ',"
    ","
    "].join("")};return c[b]&&(b=c[b]),b||(b=c.simple),b="string"==typeof b?a(b).appendTo(this.wrapper):a(b),a.extend(b,{bar:b.find(".scroll-bar"),size:b.find(".scroll-element_size"),track:b.find(".scroll-element_track")}),b},_handleMouseDown:function(b,c){var d=this.namespace;return a(document).on("blur"+d,function(){a(document).add("body").off(d),b&&b()}),a(document).on("dragstart"+d,function(a){return a.preventDefault(),!1}),a(document).on("mouseup"+d,function(){a(document).add("body").off(d),b&&b()}),a("body").on("selectstart"+d,function(a){return a.preventDefault(),!1}),c&&c.preventDefault(),!1},_updateScroll:function(b,d){var e=this.container,f=this.containerWrapper||e,g="scroll-scroll"+b+"_visible",h="x"===b?this.scrolly:this.scrollx,i=parseInt(this.container.css("x"===b?"left":"top"),10)||0,j=this.wrapper,k=d.size,l=d.visible+i;d.isVisible=k-l>1,d.isVisible?(d.scroll.addClass(g),h.scroll.addClass(g),f.addClass(g)):(d.scroll.removeClass(g),h.scroll.removeClass(g),f.removeClass(g)),"y"===b&&(e.is("textarea")||k10?(window.console&&console.log("Scroll updates exceed 10"),g=function(){}):(clearTimeout(a),a=setTimeout(g,300))}}();window.angular&&!function(a){a.module("jQueryScrollbar",[]).provider("jQueryScrollbar",function(){var b=d;return{setOptions:function(c){a.extend(b,c)},$get:function(){return{options:a.copy(b)}}}}).directive("jqueryScrollbar",["jQueryScrollbar","$parse",function(a,b){return{restrict:"AC",link:function(c,d,e){var f=b(e.jqueryScrollbar),g=f(c);d.scrollbar(g||a.options).on("$destroy",function(){d.scrollbar("destroy")})}}}])}(window.angular)}); \ No newline at end of file diff --git a/data/interfaces/default/js/script.js b/data/interfaces/default/js/script.js index 2df5daeb..9f2a92eb 100644 --- a/data/interfaces/default/js/script.js +++ b/data/interfaces/default/js/script.js @@ -288,23 +288,10 @@ function isPrivateIP(ip_address) { } function humanTime(seconds) { - var d = Math.floor(moment.duration(seconds, 'seconds').asDays()); - var h = Math.floor(moment.duration((seconds % 86400), 'seconds').asHours()); - var m = Math.round(moment.duration(((seconds % 86400) % 3600), 'seconds').asMinutes()); - - var text = ''; - if (d > 0) { - text = '

    ' + d + '

    day' + ((d > 1) ? 's' : '') + '

    ' - + '

    ' + h + '

    hr' + ((h > 1) ? 's' : '') + '

    ' - + '

    ' + m + '

    min' + ((m > 1) ? 's' : '') + '

    '; - } else if (h > 0) { - text = '

    ' + h + '

    hr' + ((h > 1) ? 's' : '') + '

    ' - + '

    ' + m + '

    min' + ((m > 1) ? 's' : '') + '

    '; - } else { - text = '

    ' + m + '

    min' + ((m > 1) ? 's' : '') + '

    '; + if (seconds > 0) { + return humanDuration(seconds * 1000).replaceAll(/(\d+) (\w+)/g, '

    $1

    $2

    ') } - - return text + return "

    0

    mins

    "; } String.prototype.toProperCase = function () { @@ -360,7 +347,8 @@ function humanDuration(ms, sig='dhm', units='ms', return_seconds=300000) { sig = 'dhms' } - ms = ms * factors[units]; + r = factors[sig.slice(-1)]; + ms = Math.round(ms * factors[units] / r) * r; h = ms % factors['d']; d = Math.trunc(ms / factors['d']); @@ -929,3 +917,50 @@ $('.modal').on('hide.bs.modal', function (e) { $.fn.hasScrollBar = function() { return this.get(0).scrollHeight > this.get(0).clientHeight; } + +function paginateScroller(scrollerId, buttonClass) { + $(buttonClass).click(function (e) { + e.preventDefault(); + var scroller = $(scrollerId + "-row-scroller"); + var scrollerParent = scroller.parent(); + var containerWidth = scrollerParent.width(); + var scrollCurrent = scrollerParent.scrollLeft(); + var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175; + var scrollMax = scroller.width() - Math.abs(scrollAmount); + var scrollTotal = Math.min(parseInt(scrollCurrent / 175) * 175 + scrollAmount, scrollMax); + scrollerParent.animate({ scrollLeft: scrollTotal }, 250); + }); +} + +function highlightScrollerButton(scrollerId) { + var scroller = $(scrollerId + "-row-scroller"); + var scrollerParent = scroller.parent(); + var buttonLeft = $(scrollerId + "-page-left"); + var buttonRight = $(scrollerId + "-page-right"); + + var numElems = scroller.find("li").length; + scroller.width(numElems * 175); + $(buttonLeft).addClass("disabled").blur(); + if (scroller.width() > scrollerParent.width()) { + $(buttonRight).removeClass("disabled"); + } else { + $(buttonRight).addClass("disabled"); + } + + scrollerParent.scroll(function () { + var scrollCurrent = $(this).scrollLeft(); + var scrollMax = scroller.width() - $(this).width(); + + if (scrollCurrent == 0) { + $(buttonLeft).addClass("disabled").blur(); + } else { + $(buttonLeft).removeClass("disabled"); + } + + if (scrollCurrent >= scrollMax) { + $(buttonRight).addClass("disabled").blur(); + } else { + $(buttonRight).removeClass("disabled"); + } + }); +} diff --git a/data/interfaces/default/js/tables/export_table.js b/data/interfaces/default/js/tables/export_table.js index 44fe4e13..c0f6cf2f 100644 --- a/data/interfaces/default/js/tables/export_table.js +++ b/data/interfaces/default/js/tables/export_table.js @@ -100,7 +100,7 @@ export_table_options = { "createdCell": function (td, cellData, rowData, row, col) { if (cellData !== '') { var images = ''; - if (rowData['thumb_level'] || rowData['art_level']) { + if (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level']) { images = ' + images'; } $(td).html(cellData + images); @@ -161,14 +161,14 @@ export_table_options = { if (cellData === 1 && rowData['exists']) { var tooltip_title = ''; var icon = ''; - if (rowData['thumb_level'] || rowData['art_level'] || rowData['individual_files']) { - tooltip_title = 'Zip Archive'; + if (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level'] || rowData['individual_files']) { + tooltip_title = 'ZIP Archive'; icon = 'fa-file-archive'; } else { tooltip_title = rowData['file_format'].toUpperCase() + ' File'; icon = 'fa-file-download'; } - var icon = (rowData['thumb_level'] || rowData['art_level'] || rowData['individual_files']) ? 'fa-file-archive' : 'fa-file-download'; + var icon = (rowData['thumb_level'] || rowData['art_level'] || rowData['logo_level'] || rowData['individual_files']) ? 'fa-file-archive' : 'fa-file-download'; $(td).html(''); } else if (cellData === 0) { var percent = Math.min(getPercent(rowData['exported_items'], rowData['total_items']), 99) diff --git a/data/interfaces/default/js/tables/history_table.js b/data/interfaces/default/js/tables/history_table.js index deefa067..7f9d578f 100644 --- a/data/interfaces/default/js/tables/history_table.js +++ b/data/interfaces/default/js/tables/history_table.js @@ -247,7 +247,7 @@ history_table_options = { }, { "targets": [11], - "data": "duration", + "data": "play_duration", "render": function (data, type, full) { if (data !== null) { return Math.round(moment.duration(data, 'seconds').as('minutes')) + ' mins'; @@ -263,13 +263,17 @@ history_table_options = { "targets": [12], "data": "watched_status", "createdCell": function (td, cellData, rowData, row, col) { + var circleValue = ""; if (cellData == 1) { - $(td).html(''); + circleValue = " circle-full"; + } else if (cellData == 0.75) { + circleValue = " circle-three-quarter"; } else if (cellData == 0.5) { - $(td).html(''); - } else { - $(td).html(''); + circleValue = " circle-half"; + } else if (cellData == 0.25) { + circleValue = " circle-quarter"; } + $(td).html('
    '); }, "searchable": false, "orderable": false, @@ -529,7 +533,7 @@ function childTableFormat(rowData) { 'Started' + 'Paused' + 'Stopped' + - 'Duration' + + 'Duration' + '' + '' + '' + diff --git a/data/interfaces/default/library.html b/data/interfaces/default/library.html index 59de6497..ba61153d 100644 --- a/data/interfaces/default/library.html +++ b/data/interfaces/default/library.html @@ -149,10 +149,10 @@ DOCUMENTATION :: END
    @@ -175,10 +175,10 @@ DOCUMENTATION :: END
    @@ -248,7 +248,7 @@ DOCUMENTATION :: END Started Paused Stopped - Duration + Duration @@ -690,7 +690,8 @@ DOCUMENTATION :: END }, complete: function(xhr, status) { $("#library-recently-watched").html(xhr.responseText); - highlightWatchedScrollerButton(); + highlightScrollerButton("#recently-watched"); + paginateScroller("#recently-watched", ".paginate-watched"); } }); } @@ -706,7 +707,8 @@ DOCUMENTATION :: END }, complete: function(xhr, status) { $("#library-recently-added").html(xhr.responseText); - highlightAddedScrollerButton(); + highlightScrollerButton("#recently-added"); + paginateScroller("#recently-added", ".paginate-added"); } }); } @@ -716,83 +718,8 @@ DOCUMENTATION :: END recentlyAdded(); % endif - function highlightWatchedScrollerButton() { - var scroller = $("#recently-watched-row-scroller"); - var numElems = scroller.find("li").length; - scroller.width(numElems * 175); - if (scroller.width() > $("#library-recently-watched").width()) { - $("#recently-watched-page-right").removeClass("disabled"); - } else { - $("#recently-watched-page-right").addClass("disabled"); - } - } - - function highlightAddedScrollerButton() { - var scroller = $("#recently-added-row-scroller"); - var numElems = scroller.find("li").length; - scroller.width(numElems * 175); - if (scroller.width() > $("#library-recently-added").width()) { - $("#recently-added-page-right").removeClass("disabled"); - } else { - $("#recently-added-page-right").addClass("disabled"); - } - } - - $(window).resize(function() { - highlightWatchedScrollerButton(); - highlightAddedScrollerButton(); - }); - $('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 }); - var leftTotalWatched = 0; - $(".paginate-watched").click(function (e) { - e.preventDefault(); - var scroller = $("#recently-watched-row-scroller"); - var containerWidth = $("#library-recently-watched").width(); - var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175; - var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0); - - leftTotalWatched = Math.max(Math.min(leftTotalWatched + scrollAmount, 0), leftMax); - scroller.animate({ left: leftTotalWatched }, 250); - - if (leftTotalWatched == 0) { - $("#recently-watched-page-left").addClass("disabled").blur(); - } else { - $("#recently-watched-page-left").removeClass("disabled"); - } - - if (leftTotalWatched == leftMax) { - $("#recently-watched-page-right").addClass("disabled").blur(); - } else { - $("#recently-watched-page-right").removeClass("disabled"); - } - }); - - var leftTotalAdded = 0; - $(".paginate-added").click(function (e) { - e.preventDefault(); - var scroller = $("#recently-added-row-scroller"); - var containerWidth = $("#library-recently-added").width(); - var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175; - var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0); - - leftTotalAdded = Math.max(Math.min(leftTotalAdded + scrollAmount, 0), leftMax); - scroller.animate({ left: leftTotalAdded }, 250); - - if (leftTotalAdded == 0) { - $("#recently-added-page-left").addClass("disabled").blur(); - } else { - $("#recently-added-page-left").removeClass("disabled"); - } - - if (leftTotalAdded == leftMax) { - $("#recently-added-page-right").addClass("disabled").blur(); - } else { - $("#recently-added-page-right").removeClass("disabled"); - } - }); - $(document).ready(function () { // Javascript to enable link to tab diff --git a/data/interfaces/default/library_recently_added.html b/data/interfaces/default/library_recently_added.html index fb53f9a5..4cf56d8e 100644 --- a/data/interfaces/default/library_recently_added.html +++ b/data/interfaces/default/library_recently_added.html @@ -36,7 +36,7 @@ DOCUMENTATION :: END %>
    -
    +