├── .gitignore ├── bt-svg-viewer ├── assets │ ├── screenshot-1.jpg │ ├── screenshot-2.jpg │ ├── screenshot-3.jpg │ ├── banner-1544x500.jpg │ └── banner-772x250.jpg ├── languages │ ├── bt-svg-viewer-de_DE.mo │ ├── bt-svg-viewer-es_ES.mo │ ├── bt-svg-viewer-fr_FR.mo │ ├── bt-svg-viewer-it_IT.mo │ ├── bt-svg-viewer.pot │ ├── bt-svg-viewer-it_IT.po │ ├── bt-svg-viewer-es_ES.po │ ├── bt-svg-viewer-fr_FR.po │ └── bt-svg-viewer-de_DE.po ├── .phpcs.xml.dist ├── .circleci │ └── config.yml ├── admin │ ├── css │ │ └── admin.css │ ├── changelog.html │ ├── help.html │ └── js │ │ └── admin.js ├── readme.txt └── css │ └── bt-svg-viewer.css ├── tests ├── wp-tests-env.php ├── js │ ├── setup.js │ └── bt-svg-viewer.test.js ├── bootstrap.php └── test-plugin.php ├── vitest.config.js ├── package.json ├── phpunit.xml.dist ├── phpcs.xml.dist ├── Rakefile ├── .github └── workflows │ └── deploy-wordpress.yml ├── composer.json ├── phpstan.neon.dist ├── .phpunit.result.cache ├── CHANGELOG.md ├── bin └── install-wp-tests.sh ├── README.md └── src └── _README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .cursor 2 | .vscode 3 | commit_message.txt 4 | vendor 5 | .DS_Store 6 | node_modules 7 | bt-svg-viewer.zip 8 | -------------------------------------------------------------------------------- /bt-svg-viewer/assets/screenshot-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/bt-svg-viewer/main/bt-svg-viewer/assets/screenshot-1.jpg -------------------------------------------------------------------------------- /bt-svg-viewer/assets/screenshot-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/bt-svg-viewer/main/bt-svg-viewer/assets/screenshot-2.jpg -------------------------------------------------------------------------------- /bt-svg-viewer/assets/screenshot-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/bt-svg-viewer/main/bt-svg-viewer/assets/screenshot-3.jpg -------------------------------------------------------------------------------- /bt-svg-viewer/assets/banner-1544x500.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/bt-svg-viewer/main/bt-svg-viewer/assets/banner-1544x500.jpg -------------------------------------------------------------------------------- /bt-svg-viewer/assets/banner-772x250.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/bt-svg-viewer/main/bt-svg-viewer/assets/banner-772x250.jpg -------------------------------------------------------------------------------- /bt-svg-viewer/languages/bt-svg-viewer-de_DE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/bt-svg-viewer/main/bt-svg-viewer/languages/bt-svg-viewer-de_DE.mo -------------------------------------------------------------------------------- /bt-svg-viewer/languages/bt-svg-viewer-es_ES.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/bt-svg-viewer/main/bt-svg-viewer/languages/bt-svg-viewer-es_ES.mo -------------------------------------------------------------------------------- /bt-svg-viewer/languages/bt-svg-viewer-fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/bt-svg-viewer/main/bt-svg-viewer/languages/bt-svg-viewer-fr_FR.mo -------------------------------------------------------------------------------- /bt-svg-viewer/languages/bt-svg-viewer-it_IT.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ttscoff/bt-svg-viewer/main/bt-svg-viewer/languages/bt-svg-viewer-it_IT.mo -------------------------------------------------------------------------------- /tests/wp-tests-env.php: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/ 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/js/setup.js: -------------------------------------------------------------------------------- 1 | import { vi } from "vitest"; 2 | 3 | // Mock window.fetch for SVG loading. 4 | global.fetch = vi.fn().mockResolvedValue({ 5 | ok: true, 6 | text: () => Promise.resolve(""), 7 | }); 8 | 9 | // Provide minimal clipboard support. 10 | Object.assign(global.navigator, { 11 | clipboard: { 12 | writeText: vi.fn().mockResolvedValue(undefined), 13 | }, 14 | }); 15 | 16 | // Prevent prompt dialogs from blocking tests. 17 | global.window.prompt = vi.fn(); 18 | 19 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | WordPress Coding Standards for WP SVG Viewer plugin. 4 | 5 | bt-svg-viewer/bt-svg-viewer-plugin.php 6 | bt-svg-viewer/admin 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | desc "Development version check" 2 | task :ver do 3 | gver = `git ver` 4 | cver = IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1] 5 | res = `grep Version: bt-svg-viewer/bt-svg-viewer-plugin.php` 6 | version = res.match(/Version: *(\d+\.\d+\.\d+)/)[1] 7 | puts "git tag: #{gver}" 8 | puts "version.rb: #{version}" 9 | puts "changelog: #{cver}" 10 | end 11 | 12 | desc 'Changelog version check' 13 | task :cver do 14 | puts IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1] 15 | end 16 | -------------------------------------------------------------------------------- /.github/workflows/deploy-wordpress.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to WordPress.org 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | deploy: 13 | name: Deploy tagged release 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Deploy plugin 22 | uses: 10up/action-wordpress-plugin-deploy@v2 23 | env: 24 | SVN_USERNAME: ${{ secrets.WPORG_SVN_USERNAME }} 25 | SVN_PASSWORD: ${{ secrets.WPORG_SVN_PASSWORD }} 26 | SLUG: bt-svg-viewer 27 | BUILD_DIR: bt-svg-viewer 28 | VERSION: ${{ github.event.release.tag_name }} 29 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "phpunit/phpunit": "^9.6", 4 | "brain/monkey": "^2.6", 5 | "wp-phpunit/wp-phpunit": "^6.8", 6 | "yoast/phpunit-polyfills": "^2.0", 7 | "phpstan/phpstan": "^1.11", 8 | "squizlabs/php_codesniffer": "^3.10", 9 | "dealerdirect/phpcodesniffer-composer-installer": "^1.0", 10 | "wp-coding-standards/wpcs": "^3.0", 11 | "php-stubs/wordpress-stubs": "^6.5" 12 | }, 13 | "scripts": { 14 | "test:php": "vendor/bin/phpunit -c phpunit.xml.dist", 15 | "lint:phpcs": "vendor/bin/phpcs", 16 | "lint:phpstan": "vendor/bin/phpstan analyse --configuration=phpstan.neon.dist", 17 | "test:all": [ 18 | "@test:php", 19 | "npm run test:js" 20 | ], 21 | "lint": [ 22 | "@lint:phpcs", 23 | "@lint:phpstan" 24 | ], 25 | "test": [ 26 | "@test:php" 27 | ] 28 | }, 29 | "config": { 30 | "allow-plugins": { 31 | "dealerdirect/phpcodesniffer-composer-installer": true 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - vendor/phpstan/phpstan/conf/config.level5.neon 3 | 4 | parameters: 5 | tmpDir: build/phpstan 6 | paths: 7 | - bt-svg-viewer/bt-svg-viewer-plugin.php 8 | - bt-svg-viewer/admin 9 | level: 5 10 | bootstrapFiles: 11 | - bt-svg-viewer/tests/bootstrap.php 12 | stubFiles: 13 | - vendor/php-stubs/wordpress-stubs/wordpress-stubs.php 14 | ignoreErrors: 15 | - '#^Function apply_filters\(\) not found.$#' 16 | - '#^Function add_action\(\) not found.$#' 17 | - '#^Function add_filter\(\) not found.$#' 18 | - '#^Function remove_filter\(\) not found.$#' 19 | - '#^Function remove_action\(\) not found.$#' 20 | - '#^Function shortcode_atts\(\) not found.$#' 21 | - '#^Function esc_url\(\) not found.$#' 22 | - '#^Function esc_url_raw\(\) not found.$#' 23 | - '#^Function wp_localize_script\(\) not found.$#' 24 | - '#^Function wp_enqueue_script\(\) not found.$#' 25 | - '#^Function wp_enqueue_style\(\) not found.$#' 26 | - '#^Function wp_style_is\(\) not found.$#' 27 | 28 | -------------------------------------------------------------------------------- /.phpunit.result.cache: -------------------------------------------------------------------------------- 1 | {"version":1,"defects":{"SVG_Viewer_Tests::test_save_preset_meta_sanitizes_values":3,"SVG_Viewer_Tests::test_get_button_color_style_declarations_sanitizes_values":3,"BT_SVG_Viewer_Tests::test_render_shortcode_uses_preset_values":3},"times":{"SVG_Viewer_Tests::test_svg_mime_type_is_added":0.001,"SVG_Viewer_Tests::test_render_shortcode_requires_source":0.002,"SVG_Viewer_Tests::test_render_shortcode_generates_button_styles_from_aliases":0.001,"SVG_Viewer_Tests::test_resolve_interaction_config_adjusts_pan_mode_and_messages":0.001,"SVG_Viewer_Tests::test_render_shortcode_uses_preset_values":0.008,"SVG_Viewer_Tests::test_shortcode_enqueue_assets":0.007,"SVG_Viewer_Tests::test_get_svg_url_variants":0.003,"SVG_Viewer_Tests::test_parse_controls_config_outputs_expected_structure":0.001,"SVG_Viewer_Tests::test_save_preset_meta_sanitizes_values":0.279,"SVG_Viewer_Tests::test_shortcode_inline_overrides_preset_values":0.021,"SVG_Viewer_Tests::test_render_shortcode_returns_error_when_preset_missing":0.001,"SVG_Viewer_Tests::test_get_button_color_style_declarations_sanitizes_values":0.001,"SVG_Viewer_Tests::test_adjust_color_brightness_handles_extremes":0.001,"SVG_Viewer_Tests::test_build_style_attribute_trims_duplicates":0.001,"BT_SVG_Viewer_Tests::test_shortcode_inline_overrides_preset_values":0.024,"BT_SVG_Viewer_Tests::test_render_shortcode_returns_error_when_preset_missing":0.002,"BT_SVG_Viewer_Tests::test_get_button_color_style_declarations_sanitizes_values":0.001,"BT_SVG_Viewer_Tests::test_adjust_color_brightness_handles_extremes":0.001,"BT_SVG_Viewer_Tests::test_build_style_attribute_trims_duplicates":0.002,"BT_SVG_Viewer_Tests::test_btsvviewer_mime_type_is_added":0.001,"BT_SVG_Viewer_Tests::test_render_shortcode_requires_source":0.002,"BT_SVG_Viewer_Tests::test_render_shortcode_generates_button_styles_from_aliases":0.001,"BT_SVG_Viewer_Tests::test_resolve_interaction_config_adjusts_pan_mode_and_messages":0.001,"BT_SVG_Viewer_Tests::test_render_shortcode_uses_preset_values":0.023,"BT_SVG_Viewer_Tests::test_shortcode_enqueue_assets":0.025}} -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | Generally-applicable sniffs for WordPress plugins. 4 | 5 | 6 | . 7 | /vendor/ 8 | /node_modules/ 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /bt-svg-viewer/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | workflows: 2 | version: 2 3 | main: 4 | jobs: 5 | - php56-build 6 | - php70-build 7 | - php71-build 8 | - php72-build 9 | - php73-build 10 | - php74-build 11 | 12 | version: 2 13 | 14 | job-references: 15 | mysql_image: &mysql_image 16 | circleci/mysql:5.6 17 | 18 | setup_environment: &setup_environment 19 | name: "Setup Environment Variables" 20 | command: | 21 | echo "export PATH=$HOME/.composer/vendor/bin:$PATH" >> $BASH_ENV 22 | source /home/circleci/.bashrc 23 | 24 | install_dependencies: &install_dependencies 25 | name: "Install Dependencies" 26 | command: | 27 | sudo apt-get update && sudo apt-get install subversion 28 | sudo -E docker-php-ext-install mysqli 29 | sudo sh -c "printf '\ndeb http://ftp.us.debian.org/debian sid main\n' >> /etc/apt/sources.list" 30 | sudo apt-get update && sudo apt-get install mysql-client-5.7 31 | 32 | php_job: &php_job 33 | environment: 34 | - WP_TESTS_DIR: "/tmp/wordpress-tests-lib" 35 | - WP_CORE_DIR: "/tmp/wordpress/" 36 | steps: 37 | - checkout 38 | - run: *setup_environment 39 | - run: *install_dependencies 40 | - run: 41 | name: "Run Tests" 42 | command: | 43 | composer global require "phpunit/phpunit=5.7.*" 44 | composer global require wp-coding-standards/wpcs 45 | phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs 46 | phpcs 47 | rm -rf $WP_TESTS_DIR $WP_CORE_DIR 48 | bash bin/install-wp-tests.sh wordpress_test root '' 127.0.0.1 latest 49 | phpunit 50 | WP_MULTISITE=1 phpunit 51 | 52 | jobs: 53 | php56-build: 54 | <<: *php_job 55 | docker: 56 | - image: circleci/php:5.6 57 | - image: *mysql_image 58 | steps: 59 | - checkout 60 | - run: *setup_environment 61 | - run: *install_dependencies 62 | - run: 63 | name: "Run Tests" 64 | command: | 65 | composer global require "phpunit/phpunit=5.7.*" 66 | composer global require wp-coding-standards/wpcs 67 | phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs 68 | phpcs 69 | SKIP_DB_CREATE=false 70 | rm -rf $WP_TESTS_DIR $WP_CORE_DIR 71 | bash bin/install-wp-tests.sh wordpress_test root '' 127.0.0.1 5.8 $SKIP_DB_CREATE 72 | phpunit 73 | WP_MULTISITE=1 phpunit 74 | SKIP_DB_CREATE=true 75 | rm -rf $WP_TESTS_DIR $WP_CORE_DIR 76 | bash bin/install-wp-tests.sh wordpress_test root '' 127.0.0.1 latest $SKIP_DB_CREATE 77 | phpunit 78 | WP_MULTISITE=1 phpunit 79 | SKIP_DB_CREATE=true 80 | rm -rf $WP_TESTS_DIR $WP_CORE_DIR 81 | bash bin/install-wp-tests.sh wordpress_test root '' 127.0.0.1 trunk $SKIP_DB_CREATE 82 | phpunit 83 | WP_MULTISITE=1 phpunit 84 | SKIP_DB_CREATE=true 85 | 86 | php70-build: 87 | <<: *php_job 88 | docker: 89 | - image: circleci/php:7.0 90 | - image: *mysql_image 91 | 92 | php71-build: 93 | <<: *php_job 94 | docker: 95 | - image: circleci/php:7.1 96 | - image: *mysql_image 97 | 98 | php72-build: 99 | <<: *php_job 100 | docker: 101 | - image: circleci/php:7.2 102 | - image: *mysql_image 103 | 104 | php73-build: 105 | <<: *php_job 106 | docker: 107 | - image: circleci/php:7.3 108 | - image: *mysql_image 109 | 110 | php74-build: 111 | <<: *php_job 112 | docker: 113 | - image: circleci/php:7.4 114 | - image: *mysql_image 115 | -------------------------------------------------------------------------------- /bt-svg-viewer/admin/css/admin.css: -------------------------------------------------------------------------------- 1 | .bt-svg-viewer-tabs { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 16px; 5 | } 6 | 7 | .bt-svg-viewer-tab-nav { 8 | display: flex; 9 | gap: 8px; 10 | border-bottom: 1px solid #dcdcde; 11 | } 12 | 13 | .bt-svg-viewer-tab-button { 14 | background: #f6f7f7; 15 | border: 1px solid #dcdcde; 16 | border-bottom: none; 17 | padding: 6px 12px; 18 | cursor: pointer; 19 | color: #2c3338; 20 | border-top-left-radius: 4px; 21 | border-top-right-radius: 4px; 22 | font-weight: 500; 23 | } 24 | 25 | .bt-svg-viewer-tab-button.is-active { 26 | background: #fff; 27 | color: #1d2327; 28 | font-weight: 600; 29 | } 30 | 31 | .bt-svg-viewer-tab-button:focus { 32 | outline: 2px solid #2271b1; 33 | outline-offset: 1px; 34 | } 35 | 36 | .bt-svg-viewer-tab-panels { 37 | border: 1px solid #dcdcde; 38 | border-radius: 4px; 39 | background: #fff; 40 | padding: 20px; 41 | } 42 | 43 | .bt-svg-viewer-tab-panel { 44 | display: none; 45 | } 46 | 47 | .bt-svg-viewer-tab-panel.is-active { 48 | display: block; 49 | } 50 | 51 | .bt-svg-viewer-help-content { 52 | max-height: 70vh; 53 | overflow-y: auto; 54 | } 55 | 56 | .bt-svg-viewer-help-content table { 57 | border-collapse: collapse; 58 | width: 100%; 59 | } 60 | 61 | .bt-svg-viewer-help-content table, 62 | .bt-svg-viewer-help-content th, 63 | .bt-svg-viewer-help-content td { 64 | border: 1px solid #dcdcde; 65 | } 66 | 67 | .bt-svg-viewer-help-content th, 68 | .bt-svg-viewer-help-content td { 69 | padding: 8px; 70 | text-align: left; 71 | } 72 | 73 | .bt-svg-viewer-admin-meta { 74 | display: flex; 75 | flex-direction: column; 76 | gap: 20px; 77 | } 78 | 79 | .bt-svg-viewer-shortcode-display { 80 | display: flex; 81 | flex-direction: column; 82 | gap: 6px; 83 | } 84 | 85 | .svg-shortcode-wrap { 86 | display: flex; 87 | align-items: center; 88 | gap: 8px; 89 | flex-wrap: wrap; 90 | } 91 | 92 | .svg-shortcode-input { 93 | flex: 1 1 240px; 94 | min-width: 200px; 95 | } 96 | 97 | .svg-shortcode-status { 98 | font-size: 12px; 99 | color: #2271b1; 100 | } 101 | 102 | .svg-shortcode-column { 103 | display: flex; 104 | align-items: center; 105 | gap: 8px; 106 | flex-wrap: wrap; 107 | } 108 | 109 | .svg-shortcode-column code { 110 | background: #f6f7f7; 111 | padding: 3px 6px; 112 | border-radius: 3px; 113 | } 114 | 115 | .bt-svg-viewer-field { 116 | display: flex; 117 | flex-direction: column; 118 | gap: 6px; 119 | } 120 | 121 | .bt-svg-viewer-field-group { 122 | display: grid; 123 | grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); 124 | gap: 16px; 125 | } 126 | 127 | .bt-svg-viewer-media-control { 128 | display: flex; 129 | gap: 8px; 130 | align-items: center; 131 | } 132 | 133 | .bt-svg-viewer-media-control input[type="text"] { 134 | flex: 1; 135 | } 136 | 137 | .bt-svg-viewer-admin-preview { 138 | border: 1px solid #dcdcde; 139 | border-radius: 4px; 140 | background-color: #fff; 141 | overflow: hidden; 142 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); 143 | } 144 | 145 | .bt-svg-viewer-admin-preview-toolbar { 146 | display: flex; 147 | align-items: center; 148 | gap: 8px; 149 | padding: 12px 16px; 150 | background-color: #f6f7f7; 151 | border-bottom: 1px solid #dcdcde; 152 | } 153 | 154 | .bt-svg-viewer-admin-preview-toolbar .svg-admin-status { 155 | margin-left: auto; 156 | font-size: 13px; 157 | color: #2271b1; 158 | } 159 | 160 | .bt-svg-viewer-admin-wrapper { 161 | border: none; 162 | border-radius: 0; 163 | } 164 | 165 | .bt-svg-viewer-admin-wrapper .bt-svg-viewer-title, 166 | .bt-svg-viewer-admin-wrapper .bt-svg-viewer-caption { 167 | padding: 10px 16px; 168 | } 169 | 170 | .bt-svg-viewer-admin-wrapper .bt-svg-viewer-caption { 171 | border-top: 1px solid #dcdcde; 172 | } 173 | 174 | .bt-svg-viewer-admin-wrapper .svg-controls { 175 | background: #f9f9f9; 176 | } 177 | 178 | .bt-svg-viewer-admin-wrapper .svg-container { 179 | max-height: none; 180 | } 181 | 182 | .bt-svg-viewer-admin-meta .description { 183 | margin: 0; 184 | } 185 | 186 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.0.22 2 | 3 | 2025-11-28 04:26 4 | 5 | ### 1.0.21 6 | 7 | 2025-11-28 04:25 8 | 9 | ### 1.0.20 10 | 11 | 2025-11-28 04:17 12 | 13 | #### CHANGED 14 | 15 | - Rename shortcode hooks, options, post types, and meta keys to the btsvviewer prefix with automatic migration for legacy data. 16 | - Continue honoring existing [svg_viewer] embeds by registering the legacy shortcode and admin-post handlers alongside the new ones. 17 | 18 | #### IMPROVED 19 | 20 | - Scope localized JS globals (btsvviewerConfig, btsvviewerAdmin, window.btsvviewerInstances) to avoid conflicts with other plugins. 21 | - Embed the resolved SVG URL in markup data attributes so presets and tests can read it without extra JavaScript. 22 | 23 | #### FIXED 24 | 25 | - Align unit tests and runtime assets with the renamed identifiers to keep the suite green. 26 | 27 | ### 1.0.19 28 | 29 | 2025-11-19 10:40 30 | 31 | #### CHANGED 32 | 33 | - Changed class name to use plugin prefix 34 | 35 | ### 1.0.18 36 | 37 | 2025-11-19 10:39 38 | 39 | #### CHANGED 40 | 41 | - Changed class name to use plugin prefix 42 | 43 | ### 1.0.17 44 | 45 | 2025-11-19 10:28 46 | 47 | ### 1.0.16 48 | 49 | 2025-11-19 10:22 50 | 51 | #### CHANGED 52 | 53 | - Prefix the main PHP surface (BTSVVI_Viewer, option keys, hooks) to avoid naming collisions. 54 | 55 | #### IMPROVED 56 | 57 | - More extensive test suite 58 | - Initialize viewer instances via enqueued scripts and a JS queue to remove inline JS. 59 | 60 | #### FIXED 61 | 62 | - Sanitize and validate all defaults and preset inputs before saving. 63 | - Block direct file access on the bootstrap path and harden cached admin markup handling. 64 | 65 | ### 1.0.14 66 | 67 | 2025-11-10 07:44 68 | 69 | #### CHANGED 70 | 71 | - Rename plugin and all text domains to BT SVG Viewer 72 | 73 | #### IMPROVED 74 | 75 | - Add screenshots for Plugin repository display 76 | 77 | ### 1.0.13 78 | 79 | 2025-11-10 05:24 80 | 81 | #### FIXED 82 | 83 | - Fix readme.txt for plugin submission 84 | 85 | ### 1.0.12 86 | 87 | 2025-11-10 04:59 88 | 89 | #### FIXED 90 | 91 | - Rename without WP for plugin submission 92 | 93 | ### 1.0.10 94 | 95 | 2025-11-09 13:24 96 | 97 | #### IMPROVED 98 | 99 | - Add plugins page settings link 100 | 101 | ### 1.0.9 102 | 103 | 2025-11-09 11:20 104 | 105 | #### FIXED 106 | 107 | - Broken icon/button text after plugin submission fixes 108 | 109 | ### 1.0.8 110 | 111 | 2025-11-09 10:33 112 | 113 | ### 1.0.7 114 | 115 | 2025-11-09 10:28 116 | 117 | #### NEW 118 | 119 | - Document shortcode aliases for button colors 120 | - Provide example shortcode using button_bg/button_fg aliases 121 | - Pan:drag and zoom:scroll behaviors 122 | - Pan/zoom behavior shortcode options and gesture captions 123 | - Update plugin readme highlights and upgrade notice for animated zoom release 124 | 125 | #### IMPROVED 126 | 127 | - Note preset color pickers for fill/border/foreground 128 | - Add styling guidance for CSS custom properties 129 | - Lock zoom to coordinates to prevent drift 130 | - Smooth transition for zoom and pan 131 | - Cache-busting toggle and smooth zoom behaviour in project docs 132 | - Add new options to localizations 133 | - Disable zoom in/out buttons when at min/max zoom 134 | - Push center coordinates to live preview so center button works immediately after saving zoom/position coordinates 135 | - Admin auto-centers newly selected SVGs for easier tweaking 136 | - Zoom controls snap to min/max and clearly show disabled states 137 | 138 | #### FIXED 139 | 140 | - Captured center values now match the Center View preview 141 | - Preset pan mode overrides persist when switching between scroll and drag 142 | 143 | ### 1.1.0 144 | 145 | 2025-11-09 14:10 146 | 147 | #### NEW 148 | 149 | - Pan and zoom interaction modes configurable via shortcode/preset, with automatic gesture captions. 150 | - Smooth, cursor-focused zoom animations for wheel, slider, and modifier-click gestures. 151 | - Defaults tab option to enable asset cache busting for debugging (auto-enabled on `dev.*` / `wptest.*` domains). 152 | 153 | #### IMPROVED 154 | 155 | - Drag panning now tracks 1:1 with the pointer and ignores stray wheel input. 156 | - Zooming keeps the focus point locked under the cursor while preventing unintended panning. 157 | 158 | ### 1.0.6 159 | 160 | 2025-11-08 10:35 161 | 162 | #### FIXED 163 | 164 | - CSS caching not keeping up with versions 165 | 166 | ### 1.0.5 167 | 168 | 2025-11-08 10:13 169 | 170 | #### FIXED 171 | 172 | - Bad permissions in zip distro 173 | 174 | ### 1.0.4 175 | 176 | 2025-11-08 08:54 177 | 178 | #### FIXED 179 | 180 | - Fixing deploy process 181 | 182 | ### 1.0.3 183 | 184 | 2025-11-08 08:47 185 | 186 | ### FIXED 187 | 188 | - Changelog duplication 189 | 190 | ### 1.0.2 191 | 192 | 2025-11-08 08:39 193 | 194 | #### NEW 195 | 196 | - DE, ES, FR, IT localizations 197 | - Help and Changelog tabs in admin panel 198 | 199 | #### IMPROVED 200 | 201 | - Major README overhaul 202 | 203 | ### 1.0.1 204 | 205 | 2025-11-08 06:40 206 | 207 | #### NEW 208 | 209 | - Admin panel where you can create custom SVG posts with all configuration options stored 210 | - Buttons can be configured to left, right, top, bottom 211 | - Buttons can be configured to icon, text, or both 212 | - Buttons can be configured with custom settings to display only certain buttons, define left/right alignment, and more 213 | - Preview in admin panel shows how buttons will appear 214 | - Preview in admin panel can be panned/zoomed and then its current state can be saved as the initial state for the front-end display 215 | 216 | ## BT SVG Viewer 1.0.0 217 | 218 | 2025-11-07 08:00 219 | 220 | #### NEW 221 | 222 | - Initial release 223 | - Shortcode for creating an SVG viewer with zoom and pan 224 | -------------------------------------------------------------------------------- /bt-svg-viewer/languages/bt-svg-viewer.pot: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: BT SVG Viewer 1.0.1\n" 4 | "Report-Msgid-Bugs-To: https://github.com/ttscoff/bt-svg-viewer/issues\n" 5 | "POT-Creation-Date: 2025-11-08 00:00+0000\n" 6 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 7 | "Last-Translator: \n" 8 | "Language-Team: \n" 9 | "Language: \n" 10 | "MIME-Version: 1.0\n" 11 | "Content-Type: text/plain; charset=UTF-8\n" 12 | "Content-Transfer-Encoding: 8bit\n" 13 | "X-Generator: GPT-5 Codex\n" 14 | "X-Domain: bt-svg-viewer\n" 15 | 16 | #: bt-svg-viewer-plugin.php 17 | msgid "Error: SVG preset not found for ID %s." 18 | msgstr "" 19 | 20 | #: bt-svg-viewer-plugin.php 21 | msgid "Error: SVG source not specified. Use [btsvviewer src=\"path/to/file.svg\"]" 22 | msgstr "" 23 | 24 | #: bt-svg-viewer-plugin.php 25 | msgid "Error: Invalid SVG path." 26 | msgstr "" 27 | 28 | #: bt-svg-viewer-plugin.php 29 | msgid "BT SVG Viewer Presets" 30 | msgstr "" 31 | 32 | #: bt-svg-viewer-plugin.php 33 | msgid "BT SVG Viewer Preset" 34 | msgstr "" 35 | 36 | #: bt-svg-viewer-plugin.php 37 | msgid "BT SVG Viewer" 38 | msgstr "" 39 | 40 | #: bt-svg-viewer-plugin.php 41 | msgid "Add New Preset" 42 | msgstr "" 43 | 44 | #: bt-svg-viewer-plugin.php 45 | msgid "Add New BT SVG Viewer Preset" 46 | msgstr "" 47 | 48 | #: bt-svg-viewer-plugin.php 49 | msgid "Edit BT SVG Viewer Preset" 50 | msgstr "" 51 | 52 | #: bt-svg-viewer-plugin.php 53 | msgid "New BT SVG Viewer Preset" 54 | msgstr "" 55 | 56 | #: bt-svg-viewer-plugin.php 57 | msgid "View BT SVG Viewer Preset" 58 | msgstr "" 59 | 60 | #: bt-svg-viewer-plugin.php 61 | msgid "Search BT SVG Viewer Presets" 62 | msgstr "" 63 | 64 | #: bt-svg-viewer-plugin.php 65 | msgid "No presets found" 66 | msgstr "" 67 | 68 | #: bt-svg-viewer-plugin.php 69 | msgid "No presets found in trash" 70 | msgstr "" 71 | 72 | #: bt-svg-viewer-plugin.php 73 | msgid "Please select an SVG before loading the preview." 74 | msgstr "" 75 | 76 | #: bt-svg-viewer-plugin.php 77 | msgid "Captured viewer state from the preview." 78 | msgstr "" 79 | 80 | #: bt-svg-viewer-plugin.php 81 | msgid "Unable to capture the current state. Refresh the preview and try again." 82 | msgstr "" 83 | 84 | #: bt-svg-viewer-plugin.php 85 | msgid "Shortcode copied to clipboard." 86 | msgstr "" 87 | 88 | #: bt-svg-viewer-plugin.php 89 | msgid "Press ⌘/Ctrl+C to copy the shortcode." 90 | msgstr "" 91 | 92 | #: bt-svg-viewer-plugin.php 93 | msgid "BT SVG Viewer Settings" 94 | msgstr "" 95 | 96 | #: bt-svg-viewer-plugin.php 97 | msgid "Settings" 98 | msgstr "" 99 | 100 | #: bt-svg-viewer-plugin.php 101 | msgid "Help" 102 | msgstr "" 103 | 104 | #: bt-svg-viewer-plugin.php 105 | msgid "Preset Shortcode" 106 | msgstr "" 107 | 108 | #: bt-svg-viewer-plugin.php 109 | msgid "Copy" 110 | msgstr "" 111 | 112 | #: bt-svg-viewer-plugin.php 113 | msgid "Use this shortcode in pages or posts to embed this preset." 114 | msgstr "" 115 | 116 | #: bt-svg-viewer-plugin.php 117 | msgid "SVG Source URL" 118 | msgstr "" 119 | 120 | #: bt-svg-viewer-plugin.php 121 | msgid "https://example.com/my-graphic.svg or uploads/2025/graphic.svg" 122 | msgstr "" 123 | 124 | #: bt-svg-viewer-plugin.php 125 | msgid "Select SVG" 126 | msgstr "" 127 | 128 | #: bt-svg-viewer-plugin.php 129 | msgid "Viewer Height" 130 | msgstr "" 131 | 132 | #: bt-svg-viewer-plugin.php 133 | msgid "Min Zoom (%)" 134 | msgstr "" 135 | 136 | #: bt-svg-viewer-plugin.php 137 | msgid "Max Zoom (%)" 138 | msgstr "" 139 | 140 | #: bt-svg-viewer-plugin.php 141 | msgid "Initial Zoom (%)" 142 | msgstr "" 143 | 144 | #: bt-svg-viewer-plugin.php 145 | msgid "Zoom Increment (%)" 146 | msgstr "" 147 | 148 | #: bt-svg-viewer-plugin.php 149 | msgid "Center X" 150 | msgstr "" 151 | 152 | #: bt-svg-viewer-plugin.php 153 | msgid "Center Y" 154 | msgstr "" 155 | 156 | #: bt-svg-viewer-plugin.php 157 | msgid "Controls Position" 158 | msgstr "" 159 | 160 | #: bt-svg-viewer-plugin.php 161 | msgid "Top" 162 | msgstr "" 163 | 164 | #: bt-svg-viewer-plugin.php 165 | msgid "Bottom" 166 | msgstr "" 167 | 168 | #: bt-svg-viewer-plugin.php 169 | msgid "Left" 170 | msgstr "" 171 | 172 | #: bt-svg-viewer-plugin.php 173 | msgid "Right" 174 | msgstr "" 175 | 176 | #: bt-svg-viewer-plugin.php 177 | msgid "Controls Buttons/Layout" 178 | msgstr "" 179 | 180 | #: bt-svg-viewer-plugin.php 181 | msgid "" 182 | "Combine multiple options with commas. Examples: both, icon, text, compact, " 183 | "labels-on-hover, minimal, alignleft, aligncenter, alignright, custom,both," 184 | "aligncenter,zoom_in,zoom_out,reset,center,coords" 185 | msgstr "" 186 | 187 | #: bt-svg-viewer-plugin.php 188 | msgid "Title (optional)" 189 | msgstr "" 190 | 191 | #: bt-svg-viewer-plugin.php 192 | msgid "Caption (optional)" 193 | msgstr "" 194 | 195 | #: bt-svg-viewer-plugin.php 196 | msgid "Supports basic HTML formatting." 197 | msgstr "" 198 | 199 | #: bt-svg-viewer-plugin.php 200 | msgid "Load / Refresh Preview" 201 | msgstr "" 202 | 203 | #: bt-svg-viewer-plugin.php 204 | msgid "Use Current View for Initial State" 205 | msgstr "" 206 | 207 | #: bt-svg-viewer-plugin.php 208 | msgid "Help content is not available. Run the \"Render Help\" build step to regenerate it." 209 | msgstr "" 210 | 211 | #: bt-svg-viewer-plugin.php 212 | msgid "Zoom In" 213 | msgstr "" 214 | 215 | #: bt-svg-viewer-plugin.php 216 | msgid "Zoom In (Ctrl +)" 217 | msgstr "" 218 | 219 | #: bt-svg-viewer-plugin.php 220 | msgid "Zoom Out" 221 | msgstr "" 222 | 223 | #: bt-svg-viewer-plugin.php 224 | msgid "Zoom Out (Ctrl -)" 225 | msgstr "" 226 | 227 | #: bt-svg-viewer-plugin.php 228 | msgid "Reset Zoom" 229 | msgstr "" 230 | 231 | #: bt-svg-viewer-plugin.php 232 | msgid "Center View" 233 | msgstr "" 234 | 235 | #: bt-svg-viewer-plugin.php 236 | msgid "Copy Center" 237 | msgstr "" 238 | 239 | #: bt-svg-viewer-plugin.php 240 | msgid "Copy current center coordinates" 241 | msgstr "" 242 | 243 | #: bt-svg-viewer-plugin.php 244 | msgid "Shortcode" 245 | msgstr "" 246 | 247 | -------------------------------------------------------------------------------- /bt-svg-viewer/readme.txt: -------------------------------------------------------------------------------- 1 | === BT SVG Viewer === 2 | Contributors: bterp 3 | Donate link: https://brettterpstra.com/donate/ 4 | Tags: svg, shortcode, maps, zoom, viewer 5 | Requires at least: 5.8 6 | Tested up to: 6.8 7 | Requires PHP: 7.4 8 | Stable tag: 1.0.22 9 | License: GPLv2 or later 10 | License URI: https://www.gnu.org/licenses/gpl-2.0.html 11 | 12 | A shortcode-powered SVG viewer with reusable presets, zoom, and pan controls. 13 | 14 | == Description == 15 | 16 | Embed large, detailed SVG diagrams in WordPress with an accessible viewer that supports touch-friendly zooming, panning, and centering. BT SVG Viewer keeps maps, technical drawings, and infographics sharp on every screen, while giving editors a visual preset builder so they can reuse configurations without repeating shortcode attributes. 17 | 18 | = Highlights = 19 | * Interactive zoom and pan controls with keyboard shortcuts and optional slider mode. 20 | * Smooth, cursor-focused zoom animations with click-drag panning. 21 | * Preset post type with live preview, copy-ready shortcodes, and color pickers for control styling. 22 | * Fine-grained shortcode attributes for height, zoom bounds, center coordinates, titles, and captions. 23 | * Front-end wrapper classes and CSS custom properties for deep theme integration. 24 | * Localized UI and strings in English, German, Spanish, French, and Italian. 25 | 26 | = Shortcode Overview = 27 | Use the `[btsvviewer]` shortcode anywhere shortcodes are supported. Pass a direct SVG URL or reference a saved preset ID. All shortcode attributes (height, zoom limits, controls layout, button colors, etc.) can be overridden per instance. See the plugin’s admin Help tab or project README for a full attribute reference and examples. Existing shortcodes created before the rename continue to render, so archived posts don’t need to be touched. 28 | 29 | = Preset Workflow = 30 | Create a preset from **BT SVG Viewer → Presets** in the admin. Upload an SVG, tune the controls, tweak button colors, then load the preview to dial in the initial zoom and center point. Save the preset and drop the generated `[btsvviewer id="123"]` shortcode wherever you need the viewer. Inline attributes always win over stored preset values, making it easy to reuse a baseline configuration. 31 | 32 | == Localization == 33 | 34 | Available translations: German (de_DE), Spanish (es_ES), French (fr_FR), Italian (it_IT). 35 | 36 | Translations were initially produced with AI assistance; corrections are welcome via https://github.com/ttscoff/bt-svg-viewer/issues. 37 | 38 | == Installation == 39 | 40 | 1. Upload the `bt-svg-viewer` folder to the `/wp-content/plugins/` directory, or install the ZIP archive via **Plugins → Add New → Upload Plugin**. 41 | 2. Activate the plugin through the **Plugins** menu in WordPress. 42 | 3. Go to **BT SVG Viewer → Presets** to create your first reusable configuration, or place the basic shortcode `[btsvviewer src="/wp-content/uploads/diagram.svg"]` in a post or page. 43 | 44 | == Frequently Asked Questions == 45 | 46 | = Why doesn’t my SVG appear in the viewer? = 47 | Make sure the SVG URL is reachable by the browser. Open the path in a new tab to confirm it loads without authentication. The plugin accepts absolute URLs, `/wp-content/...` paths, and upload-relative paths (e.g. `2025/map.svg`). Also confirm the SVG includes a valid `viewBox` so it can scale correctly. 48 | 49 | = Can I override preset values inside the shortcode? = 50 | Yes. Attributes you place in the shortcode always override the stored preset settings. For example, `[btsvviewer id="42" controls_buttons="icon,zoom_in,zoom_out"]` keeps the preset’s other values but changes the control layout. 51 | 52 | = How do I customize button colors? = 53 | Use the preset color pickers or shortcode aliases such as `button_fill`, `button_border`, and `button_foreground`. Legacy aliases `button_bg` and `button_fg` still work and map to the new properties. You can also target the wrapper’s CSS custom properties (e.g. `--bt-svg-viewer-button-fill`) in your theme. 54 | 55 | = Can I add multiple SVG viewers on one page? = 56 | Absolutely. Each `[btsvviewer]` instance manages its own state, so you can embed as many as you like on the same post or template. 57 | 58 | = Does the plugin sanitize inline SVG icons? = 59 | All bundled icon markup runs through `wp_kses` to keep the controls safe. Uploaded SVG files are not altered, so continue following your organization’s SVG hygiene best practices. 60 | 61 | == Screenshots == 62 | 63 | 1. Preset editor with live preview, button color pickers, and copy-ready shortcode helper. 64 | 2. Front-end SVG viewer displaying zoom controls and caption beneath a mind map. 65 | 3. Icon-only controls stacked along the right edge using the compact preset layout. 66 | 67 | == Changelog == 68 | 69 | = 1.0.12 = 70 | * Add shortcode/preset pan and zoom modes with automatic gesture captions for visitors. 71 | * Animate zoom transitions while keeping the pointer focus locked under the cursor. 72 | * Add an “Enable asset cache busting for debugging” toggle (auto-enabled on `dev.*`/`wptest.*` hosts). 73 | * Make drag panning 1:1 with the pointer and ignore stray wheel input during drags. 74 | * Prevent unintended panning while zooming so the focus point stays anchored. 75 | 76 | = 1.0.7 = 77 | * Document shortcode aliases for button colors and include full examples in the help docs. 78 | * Explain new pan/zoom behaviour options (drag vs scroll, gesture captions) across the docs and translations. 79 | * Lock zoom to coordinates, smooth transitions, and disable zoom buttons when limits are reached. 80 | * Auto-center new SVG selections, keep captured center values in sync with the preview, and refresh localization strings with the latest options. 81 | 82 | = 1.0.6 = 83 | * Ensure generated CSS busts cache correctly when plugin versions change. 84 | 85 | = 1.0.5 = 86 | * Fix file permissions in the distributed ZIP package. 87 | 88 | = 1.0.4 = 89 | * Stabilize the deploy pipeline to prevent incomplete releases. 90 | 91 | = 1.0.3 = 92 | * Remove duplicated changelog content in the admin tab. 93 | 94 | = 1.0.2 = 95 | * Add German, Spanish, French, and Italian translations. 96 | * Introduce Help and Changelog tabs to the presets admin screen. 97 | * Refresh the public documentation with shortcode and preset details. 98 | 99 | = 1.0.1 = 100 | * Launch the visual preset editor with live preview and reusable configurations. 101 | * Expand control options with new placement modes, icons/text toggles, and layout keywords. 102 | * Allow the preview to capture its current zoom and center for quick authoring. 103 | 104 | = 1.0.0 = 105 | * Initial release with `[btsvviewer]` shortcode, zoom controls, and pan gestures. 106 | -------------------------------------------------------------------------------- /bin/install-wp-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ $# -lt 3 ]; then 4 | echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" 5 | exit 1 6 | fi 7 | 8 | DB_NAME=$1 9 | DB_USER=$2 10 | DB_PASS=$3 11 | DB_HOST=${4-localhost} 12 | WP_VERSION=${5-latest} 13 | SKIP_DB_CREATE=${6-false} 14 | 15 | TMPDIR=${TMPDIR-/tmp} 16 | TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") 17 | WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} 18 | WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress} 19 | 20 | download() { 21 | if [ `which curl` ]; then 22 | curl -s "$1" > "$2"; 23 | elif [ `which wget` ]; then 24 | wget -nv -O "$2" "$1" 25 | else 26 | echo "Error: Neither curl nor wget is installed." 27 | exit 1 28 | fi 29 | } 30 | 31 | # Check if svn is installed 32 | check_svn_installed() { 33 | if ! command -v svn > /dev/null; then 34 | echo "Error: svn is not installed. Please install svn and try again." 35 | exit 1 36 | fi 37 | } 38 | 39 | if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then 40 | WP_BRANCH=${WP_VERSION%\-*} 41 | WP_TESTS_TAG="branches/$WP_BRANCH" 42 | 43 | elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then 44 | WP_TESTS_TAG="branches/$WP_VERSION" 45 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then 46 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 47 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 48 | WP_TESTS_TAG="tags/${WP_VERSION%??}" 49 | else 50 | WP_TESTS_TAG="tags/$WP_VERSION" 51 | fi 52 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 53 | WP_TESTS_TAG="trunk" 54 | else 55 | # http serves a single offer, whereas https serves multiple. we only want one 56 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json 57 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json 58 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') 59 | if [[ -z "$LATEST_VERSION" ]]; then 60 | echo "Latest WordPress version could not be found" 61 | exit 1 62 | fi 63 | WP_TESTS_TAG="tags/$LATEST_VERSION" 64 | fi 65 | set -ex 66 | 67 | install_wp() { 68 | 69 | if [ -d $WP_CORE_DIR ]; then 70 | return; 71 | fi 72 | 73 | mkdir -p $WP_CORE_DIR 74 | 75 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 76 | mkdir -p $TMPDIR/wordpress-trunk 77 | rm -rf $TMPDIR/wordpress-trunk/* 78 | check_svn_installed 79 | svn export --quiet https://core.svn.wordpress.org/trunk $TMPDIR/wordpress-trunk/wordpress 80 | mv $TMPDIR/wordpress-trunk/wordpress/* $WP_CORE_DIR 81 | else 82 | if [ $WP_VERSION == 'latest' ]; then 83 | local ARCHIVE_NAME='latest' 84 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then 85 | # https serves multiple offers, whereas http serves single. 86 | download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json 87 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 88 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 89 | LATEST_VERSION=${WP_VERSION%??} 90 | else 91 | # otherwise, scan the releases and get the most up to date minor version of the major release 92 | local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` 93 | LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) 94 | fi 95 | if [[ -z "$LATEST_VERSION" ]]; then 96 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 97 | else 98 | local ARCHIVE_NAME="wordpress-$LATEST_VERSION" 99 | fi 100 | else 101 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 102 | fi 103 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz 104 | tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR 105 | fi 106 | 107 | download https://raw.githubusercontent.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php 108 | } 109 | 110 | install_test_suite() { 111 | # portable in-place argument for both GNU sed and Mac OSX sed 112 | if [[ $(uname -s) == 'Darwin' ]]; then 113 | local ioption='-i.bak' 114 | else 115 | local ioption='-i' 116 | fi 117 | 118 | # set up testing suite if it doesn't yet exist 119 | if [ ! -d $WP_TESTS_DIR ]; then 120 | # set up testing suite 121 | mkdir -p $WP_TESTS_DIR 122 | rm -rf $WP_TESTS_DIR/{includes,data} 123 | check_svn_installed 124 | svn export --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes 125 | svn export --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data 126 | fi 127 | 128 | if [ ! -f wp-tests-config.php ]; then 129 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php 130 | # remove all forward slashes in the end 131 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") 132 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php 133 | sed $ioption "s:__DIR__ . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php 134 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php 135 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php 136 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php 137 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php 138 | fi 139 | 140 | } 141 | 142 | recreate_db() { 143 | shopt -s nocasematch 144 | if [[ $1 =~ ^(y|yes)$ ]] 145 | then 146 | mysqladmin drop $DB_NAME -f --user="$DB_USER" --password="$DB_PASS"$EXTRA 147 | create_db 148 | echo "Recreated the database ($DB_NAME)." 149 | else 150 | echo "Leaving the existing database ($DB_NAME) in place." 151 | fi 152 | shopt -u nocasematch 153 | } 154 | 155 | create_db() { 156 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA 157 | } 158 | 159 | install_db() { 160 | 161 | if [ ${SKIP_DB_CREATE} = "true" ]; then 162 | return 0 163 | fi 164 | 165 | # parse DB_HOST for port or socket references 166 | local PARTS=(${DB_HOST//\:/ }) 167 | local DB_HOSTNAME=${PARTS[0]}; 168 | local DB_SOCK_OR_PORT=${PARTS[1]}; 169 | local EXTRA="" 170 | 171 | if ! [ -z $DB_HOSTNAME ] ; then 172 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then 173 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" 174 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then 175 | EXTRA=" --socket=$DB_SOCK_OR_PORT" 176 | elif ! [ -z $DB_HOSTNAME ] ; then 177 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" 178 | fi 179 | fi 180 | 181 | # create database 182 | if [ $(mysql --user="$DB_USER" --password="$DB_PASS"$EXTRA --execute='show databases;' | grep ^$DB_NAME$) ] 183 | then 184 | echo "Reinstalling will delete the existing test database ($DB_NAME)" 185 | read -p 'Are you sure you want to proceed? [y/N]: ' DELETE_EXISTING_DB 186 | recreate_db $DELETE_EXISTING_DB 187 | else 188 | create_db 189 | fi 190 | } 191 | 192 | install_wp 193 | install_test_suite 194 | install_db 195 | -------------------------------------------------------------------------------- /bt-svg-viewer/languages/bt-svg-viewer-it_IT.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: BT SVG Viewer 1.0.1\n" 4 | "POT-Creation-Date: 2025-11-08 00:00+0000\n" 5 | "PO-Revision-Date: 2025-11-08 00:00+0000\n" 6 | "Last-Translator: \n" 7 | "Language-Team: Italian\n" 8 | "Language: it_IT\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 13 | "X-Generator: GPT-5 Codex\n" 14 | "X-Domain: bt-svg-viewer\n" 15 | 16 | #: bt-svg-viewer/admin/js/admin.js:35 17 | msgid "Full shortcode copied to clipboard." 18 | msgstr "Shortcode completo copiato negli appunti." 19 | 20 | #: bt-svg-viewer/admin/js/admin.js:1297 21 | msgid "Copy full shortcode" 22 | msgstr "Copia shortcode completo" 23 | 24 | #: bt-svg-viewer/bt-svg-viewer-plugin.php:647 25 | msgid "Enable asset cache busting for debugging" 26 | msgstr "Abilita il cache busting delle risorse per il debug" 27 | 28 | #: bt-svg-viewer/bt-svg-viewer-plugin.php:651 29 | msgid "" 30 | "Adds a unique suffix to script and style versions so browsers always fetch " 31 | "the latest assets. Useful for local testing; disable for production." 32 | msgstr "" 33 | "Aggiunge un suffisso univoco alle versioni di script e stili affinché i " 34 | "browser scarichino sempre le risorse più recenti. Utile per i test locali; " 35 | "disabilitalo in produzione." 36 | 37 | #: bt-svg-viewer/js/bt-svg-viewer.js:404 38 | msgid "Cmd/Ctrl-click to zoom in, Option/Alt-click to zoom out." 39 | msgstr "" 40 | "Cmd/Ctrl-clic per ingrandire, Option/Alt-clic per ridurre." 41 | 42 | #: bt-svg-viewer/js/bt-svg-viewer.js:406 43 | msgid "Scroll up to zoom in, scroll down to zoom out." 44 | msgstr "" 45 | "Scorri verso l’alto per ingrandire, verso il basso per ridurre." 46 | 47 | #: bt-svg-viewer/js/bt-svg-viewer.js:409 48 | msgid "Drag to pan around the image while scrolling zooms." 49 | msgstr "" 50 | "Trascina per spostarti nell’immagine mentre lo scorrimento esegue lo zoom." 51 | 52 | #: bt-svg-viewer/js/bt-svg-viewer.js:411 53 | msgid "Drag to pan around the image." 54 | msgstr "Trascina per spostarti nell’immagine." 55 | 56 | msgid "Error: SVG preset not found for ID %s." 57 | msgstr "Errore: preset SVG non trovato per l'ID %s." 58 | 59 | msgid "Error: SVG source not specified. Use [btsvviewer src=\"path/to/file.svg\"]" 60 | msgstr "Errore: origine SVG non specificata. Usa [btsvviewer src=\"path/to/file.svg\"]" 61 | 62 | msgid "Error: Invalid SVG path." 63 | msgstr "Errore: percorso SVG non valido." 64 | 65 | msgid "BT SVG Viewer Presets" 66 | msgstr "Preset di BT SVG Viewer" 67 | 68 | msgid "BT SVG Viewer Preset" 69 | msgstr "Preset BT SVG Viewer" 70 | 71 | msgid "BT SVG Viewer" 72 | msgstr "BT SVG Viewer" 73 | 74 | msgid "Add New Preset" 75 | msgstr "Aggiungi nuovo preset" 76 | 77 | msgid "Add New BT SVG Viewer Preset" 78 | msgstr "Aggiungi nuovo preset di BT SVG Viewer" 79 | 80 | msgid "Edit BT SVG Viewer Preset" 81 | msgstr "Modifica preset di BT SVG Viewer" 82 | 83 | msgid "New BT SVG Viewer Preset" 84 | msgstr "Nuovo preset di BT SVG Viewer" 85 | 86 | msgid "View BT SVG Viewer Preset" 87 | msgstr "Visualizza preset di BT SVG Viewer" 88 | 89 | msgid "Search BT SVG Viewer Presets" 90 | msgstr "Cerca preset di BT SVG Viewer" 91 | 92 | msgid "No presets found" 93 | msgstr "Nessun preset trovato" 94 | 95 | msgid "No presets found in trash" 96 | msgstr "Nessun preset trovato nel cestino" 97 | 98 | msgid "Please select an SVG before loading the preview." 99 | msgstr "Seleziona un SVG prima di caricare l'anteprima." 100 | 101 | msgid "Captured viewer state from the preview." 102 | msgstr "Stato del viewer acquisito dall'anteprima." 103 | 104 | msgid "Unable to capture the current state. Refresh the preview and try again." 105 | msgstr "Impossibile acquisire lo stato attuale. Aggiorna l'anteprima e riprova." 106 | 107 | msgid "Shortcode copied to clipboard." 108 | msgstr "Shortcode copiato negli appunti." 109 | 110 | msgid "Press ⌘/Ctrl+C to copy the shortcode." 111 | msgstr "Premi ⌘/Ctrl+C per copiare lo shortcode." 112 | 113 | msgid "BT SVG Viewer Settings" 114 | msgstr "Impostazioni di BT SVG Viewer" 115 | 116 | msgid "Settings" 117 | msgstr "Impostazioni" 118 | 119 | msgid "Help" 120 | msgstr "Guida" 121 | 122 | msgid "Preset Shortcode" 123 | msgstr "Shortcode del preset" 124 | 125 | msgid "Copy" 126 | msgstr "Copia" 127 | 128 | msgid "Use this shortcode in pages or posts to embed this preset." 129 | msgstr "Usa questo shortcode nelle pagine o negli articoli per incorporare questo preset." 130 | 131 | msgid "SVG Source URL" 132 | msgstr "URL sorgente SVG" 133 | 134 | msgid "https://example.com/my-graphic.svg or uploads/2025/graphic.svg" 135 | msgstr "https://example.com/my-graphic.svg oppure uploads/2025/graphic.svg" 136 | 137 | msgid "Select SVG" 138 | msgstr "Seleziona SVG" 139 | 140 | msgid "Viewer Height" 141 | msgstr "Altezza viewer" 142 | 143 | msgid "Min Zoom (%)" 144 | msgstr "Zoom minimo (%)" 145 | 146 | msgid "Max Zoom (%)" 147 | msgstr "Zoom massimo (%)" 148 | 149 | msgid "Initial Zoom (%)" 150 | msgstr "Zoom iniziale (%)" 151 | 152 | msgid "Zoom Increment (%)" 153 | msgstr "Incremento zoom (%)" 154 | 155 | msgid "Center X" 156 | msgstr "Centro X" 157 | 158 | msgid "Center Y" 159 | msgstr "Centro Y" 160 | 161 | msgid "Controls Position" 162 | msgstr "Posizione dei controlli" 163 | 164 | msgid "Top" 165 | msgstr "Alto" 166 | 167 | msgid "Bottom" 168 | msgstr "Basso" 169 | 170 | msgid "Left" 171 | msgstr "Sinistra" 172 | 173 | msgid "Right" 174 | msgstr "Destra" 175 | 176 | msgid "Controls Buttons/Layout" 177 | msgstr "Pulsanti/Layout dei controlli" 178 | 179 | msgid "" 180 | "Combine multiple options with commas. Examples: both, icon, text, compact, " 181 | "labels-on-hover, minimal, alignleft, aligncenter, alignright, custom,both," 182 | "aligncenter,zoom_in,zoom_out,reset,center,coords" 183 | msgstr "" 184 | "Combina più opzioni separate da virgole. Esempi: both, icon, text, compact, " 185 | "labels-on-hover, minimal, alignleft, aligncenter, alignright, " 186 | "custom,both,aligncenter,zoom_in,zoom_out,reset,center,coords" 187 | 188 | msgid "Title (optional)" 189 | msgstr "Titolo (facoltativo)" 190 | 191 | msgid "Caption (optional)" 192 | msgstr "Didascalia (facoltativa)" 193 | 194 | msgid "Supports basic HTML formatting." 195 | msgstr "Supporta la formattazione HTML di base." 196 | 197 | msgid "Load / Refresh Preview" 198 | msgstr "Carica / aggiorna anteprima" 199 | 200 | msgid "Use Current View for Initial State" 201 | msgstr "Usa la vista attuale come stato iniziale" 202 | 203 | msgid "" 204 | "Help content is not available. Run the \"Render Help\" build step to " 205 | "regenerate it." 206 | msgstr "" 207 | "Il contenuto della guida non è disponibile. Esegui il passaggio di build " 208 | "\"Render Help\" per rigenerarlo." 209 | 210 | msgid "Zoom In" 211 | msgstr "Zoom avanti" 212 | 213 | msgid "Zoom In (Ctrl +)" 214 | msgstr "Zoom avanti (Ctrl +)" 215 | 216 | msgid "Zoom Out" 217 | msgstr "Zoom indietro" 218 | 219 | msgid "Zoom Out (Ctrl -)" 220 | msgstr "Zoom indietro (Ctrl -)" 221 | 222 | msgid "Reset Zoom" 223 | msgstr "Reimposta zoom" 224 | 225 | msgid "Center View" 226 | msgstr "Centra vista" 227 | 228 | msgid "Copy Center" 229 | msgstr "Copia centro" 230 | 231 | msgid "Copy current center coordinates" 232 | msgstr "Copia le coordinate del centro attuale" 233 | 234 | msgid "Shortcode" 235 | msgstr "Shortcode" 236 | 237 | -------------------------------------------------------------------------------- /bt-svg-viewer/languages/bt-svg-viewer-es_ES.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: BT SVG Viewer 1.0.1\n" 4 | "POT-Creation-Date: 2025-11-08 00:00+0000\n" 5 | "PO-Revision-Date: 2025-11-08 00:00+0000\n" 6 | "Last-Translator: \n" 7 | "Language-Team: Spanish\n" 8 | "Language: es_ES\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 13 | "X-Generator: GPT-5 Codex\n" 14 | "X-Domain: bt-svg-viewer\n" 15 | 16 | #: bt-svg-viewer/admin/js/admin.js:35 17 | msgid "Full shortcode copied to clipboard." 18 | msgstr "Shortcode completo copiado al portapapeles." 19 | 20 | #: bt-svg-viewer/admin/js/admin.js:1297 21 | msgid "Copy full shortcode" 22 | msgstr "Copiar shortcode completo" 23 | 24 | #: bt-svg-viewer/bt-svg-viewer-plugin.php:647 25 | msgid "Enable asset cache busting for debugging" 26 | msgstr "Activar la ruptura de caché de recursos para depuración" 27 | 28 | #: bt-svg-viewer/bt-svg-viewer-plugin.php:651 29 | msgid "" 30 | "Adds a unique suffix to script and style versions so browsers always fetch " 31 | "the latest assets. Useful for local testing; disable for production." 32 | msgstr "" 33 | "Añade un sufijo único a las versiones de scripts y estilos para que los " 34 | "navegadores siempre obtengan los recursos más recientes. Útil para pruebas " 35 | "locales; desactívalo en producción." 36 | 37 | #: bt-svg-viewer/js/bt-svg-viewer.js:404 38 | msgid "Cmd/Ctrl-click to zoom in, Option/Alt-click to zoom out." 39 | msgstr "Cmd/Ctrl-clic para acercar, Opción/Alt-clic para alejar." 40 | 41 | #: bt-svg-viewer/js/bt-svg-viewer.js:406 42 | msgid "Scroll up to zoom in, scroll down to zoom out." 43 | msgstr "Desplaza hacia arriba para acercar, hacia abajo para alejar." 44 | 45 | #: bt-svg-viewer/js/bt-svg-viewer.js:409 46 | msgid "Drag to pan around the image while scrolling zooms." 47 | msgstr "Arrastra para desplazarte por la imagen mientras el desplazamiento hace zoom." 48 | 49 | #: bt-svg-viewer/js/bt-svg-viewer.js:411 50 | msgid "Drag to pan around the image." 51 | msgstr "Arrastra para desplazarte por la imagen." 52 | 53 | msgid "Error: SVG preset not found for ID %s." 54 | msgstr "Error: no se encontró el ajuste preestablecido SVG para el ID %s." 55 | 56 | msgid "Error: SVG source not specified. Use [btsvviewer src=\"path/to/file.svg\"]" 57 | msgstr "Error: no se especificó la fuente SVG. Usa [btsvviewer src=\"path/to/file.svg\"]" 58 | 59 | msgid "Error: Invalid SVG path." 60 | msgstr "Error: ruta SVG no válida." 61 | 62 | msgid "BT SVG Viewer Presets" 63 | msgstr "Ajustes preestablecidos de BT SVG Viewer" 64 | 65 | msgid "BT SVG Viewer Preset" 66 | msgstr "Ajuste preestablecido de BT SVG Viewer" 67 | 68 | msgid "BT SVG Viewer" 69 | msgstr "BT SVG Viewer" 70 | 71 | msgid "Add New Preset" 72 | msgstr "Añadir nuevo ajuste" 73 | 74 | msgid "Add New BT SVG Viewer Preset" 75 | msgstr "Añadir nuevo ajuste preestablecido de BT SVG Viewer" 76 | 77 | msgid "Edit BT SVG Viewer Preset" 78 | msgstr "Editar ajuste preestablecido de BT SVG Viewer" 79 | 80 | msgid "New BT SVG Viewer Preset" 81 | msgstr "Nuevo ajuste preestablecido de BT SVG Viewer" 82 | 83 | msgid "View BT SVG Viewer Preset" 84 | msgstr "Ver ajuste preestablecido de BT SVG Viewer" 85 | 86 | msgid "Search BT SVG Viewer Presets" 87 | msgstr "Buscar ajustes preestablecidos de BT SVG Viewer" 88 | 89 | msgid "No presets found" 90 | msgstr "No se encontraron ajustes" 91 | 92 | msgid "No presets found in trash" 93 | msgstr "No se encontraron ajustes en la papelera" 94 | 95 | msgid "Please select an SVG before loading the preview." 96 | msgstr "Selecciona un SVG antes de cargar la vista previa." 97 | 98 | msgid "Captured viewer state from the preview." 99 | msgstr "Estado del visor capturado desde la vista previa." 100 | 101 | msgid "Unable to capture the current state. Refresh the preview and try again." 102 | msgstr "No se puede capturar el estado actual. Actualiza la vista previa e inténtalo de nuevo." 103 | 104 | msgid "Shortcode copied to clipboard." 105 | msgstr "Shortcode copiado al portapapeles." 106 | 107 | msgid "Press ⌘/Ctrl+C to copy the shortcode." 108 | msgstr "Pulsa ⌘/Ctrl+C para copiar el shortcode." 109 | 110 | msgid "BT SVG Viewer Settings" 111 | msgstr "Ajustes de BT SVG Viewer" 112 | 113 | msgid "Settings" 114 | msgstr "Ajustes" 115 | 116 | msgid "Help" 117 | msgstr "Ayuda" 118 | 119 | msgid "Preset Shortcode" 120 | msgstr "Shortcode del ajuste" 121 | 122 | msgid "Copy" 123 | msgstr "Copiar" 124 | 125 | msgid "Use this shortcode in pages or posts to embed this preset." 126 | msgstr "Usa este shortcode en páginas o entradas para incrustar este ajuste." 127 | 128 | msgid "SVG Source URL" 129 | msgstr "URL de origen del SVG" 130 | 131 | msgid "https://example.com/my-graphic.svg or uploads/2025/graphic.svg" 132 | msgstr "https://example.com/my-graphic.svg o uploads/2025/graphic.svg" 133 | 134 | msgid "Select SVG" 135 | msgstr "Seleccionar SVG" 136 | 137 | msgid "Viewer Height" 138 | msgstr "Altura del visor" 139 | 140 | msgid "Min Zoom (%)" 141 | msgstr "Zoom mínimo (%)" 142 | 143 | msgid "Max Zoom (%)" 144 | msgstr "Zoom máximo (%)" 145 | 146 | msgid "Initial Zoom (%)" 147 | msgstr "Zoom inicial (%)" 148 | 149 | msgid "Zoom Increment (%)" 150 | msgstr "Incremento de zoom (%)" 151 | 152 | msgid "Center X" 153 | msgstr "Centro X" 154 | 155 | msgid "Center Y" 156 | msgstr "Centro Y" 157 | 158 | msgid "Controls Position" 159 | msgstr "Posición de los controles" 160 | 161 | msgid "Top" 162 | msgstr "Arriba" 163 | 164 | msgid "Bottom" 165 | msgstr "Abajo" 166 | 167 | msgid "Left" 168 | msgstr "Izquierda" 169 | 170 | msgid "Right" 171 | msgstr "Derecha" 172 | 173 | msgid "Controls Buttons/Layout" 174 | msgstr "Botones/disposición de los controles" 175 | 176 | msgid "" 177 | "Combine multiple options with commas. Examples: both, icon, text, compact, " 178 | "labels-on-hover, minimal, alignleft, aligncenter, alignright, custom,both," 179 | "aligncenter,zoom_in,zoom_out,reset,center,coords" 180 | msgstr "" 181 | "Combina varias opciones separadas por comas. Ejemplos: both, icon, text, " 182 | "compact, labels-on-hover, minimal, alignleft, aligncenter, alignright, " 183 | "custom,both,aligncenter,zoom_in,zoom_out,reset,center,coords" 184 | 185 | msgid "Title (optional)" 186 | msgstr "Título (opcional)" 187 | 188 | msgid "Caption (optional)" 189 | msgstr "Leyenda (opcional)" 190 | 191 | msgid "Supports basic HTML formatting." 192 | msgstr "Admite formato HTML básico." 193 | 194 | msgid "Load / Refresh Preview" 195 | msgstr "Cargar / actualizar vista previa" 196 | 197 | msgid "Use Current View for Initial State" 198 | msgstr "Usar la vista actual como estado inicial" 199 | 200 | msgid "" 201 | "Help content is not available. Run the \"Render Help\" build step to " 202 | "regenerate it." 203 | msgstr "" 204 | "El contenido de ayuda no está disponible. Ejecuta el paso de compilación " 205 | "\"Render Help\" para regenerarlo." 206 | 207 | msgid "Zoom In" 208 | msgstr "Acercar" 209 | 210 | msgid "Zoom In (Ctrl +)" 211 | msgstr "Acercar (Ctrl +)" 212 | 213 | msgid "Zoom Out" 214 | msgstr "Alejar" 215 | 216 | msgid "Zoom Out (Ctrl -)" 217 | msgstr "Alejar (Ctrl -)" 218 | 219 | msgid "Reset Zoom" 220 | msgstr "Restablecer zoom" 221 | 222 | msgid "Center View" 223 | msgstr "Centrar vista" 224 | 225 | msgid "Copy Center" 226 | msgstr "Copiar centro" 227 | 228 | msgid "Copy current center coordinates" 229 | msgstr "Copiar las coordenadas del centro actual" 230 | 231 | msgid "Shortcode" 232 | msgstr "Shortcode" 233 | 234 | -------------------------------------------------------------------------------- /bt-svg-viewer/languages/bt-svg-viewer-fr_FR.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: BT SVG Viewer 1.0.1\n" 4 | "POT-Creation-Date: 2025-11-08 00:00+0000\n" 5 | "PO-Revision-Date: 2025-11-08 00:00+0000\n" 6 | "Last-Translator: \n" 7 | "Language-Team: French\n" 8 | "Language: fr_FR\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 13 | "X-Generator: GPT-5 Codex\n" 14 | "X-Domain: bt-svg-viewer\n" 15 | 16 | #: bt-svg-viewer/admin/js/admin.js:35 17 | msgid "Full shortcode copied to clipboard." 18 | msgstr "Shortcode complet copié dans le presse-papiers." 19 | 20 | #: bt-svg-viewer/admin/js/admin.js:1297 21 | msgid "Copy full shortcode" 22 | msgstr "Copier le shortcode complet" 23 | 24 | #: bt-svg-viewer/bt-svg-viewer-plugin.php:647 25 | msgid "Enable asset cache busting for debugging" 26 | msgstr "Activer le cache-busting des ressources pour le débogage" 27 | 28 | #: bt-svg-viewer/bt-svg-viewer-plugin.php:651 29 | msgid "" 30 | "Adds a unique suffix to script and style versions so browsers always fetch " 31 | "the latest assets. Useful for local testing; disable for production." 32 | msgstr "" 33 | "Ajoute un suffixe unique aux versions des scripts et styles afin que les " 34 | "navigateurs récupèrent toujours les dernières ressources. Utile pour les " 35 | "tests locaux ; à désactiver en production." 36 | 37 | #: bt-svg-viewer/js/bt-svg-viewer.js:404 38 | msgid "Cmd/Ctrl-click to zoom in, Option/Alt-click to zoom out." 39 | msgstr "" 40 | "Cliquer avec Cmd/Ctrl pour zoomer, cliquer avec Option/Alt pour dézoomer." 41 | 42 | #: bt-svg-viewer/js/bt-svg-viewer.js:406 43 | msgid "Scroll up to zoom in, scroll down to zoom out." 44 | msgstr "" 45 | "Faites défiler vers le haut pour zoomer, vers le bas pour dézoomer." 46 | 47 | #: bt-svg-viewer/js/bt-svg-viewer.js:409 48 | msgid "Drag to pan around the image while scrolling zooms." 49 | msgstr "" 50 | "Faites glisser pour vous déplacer dans l’image pendant que le défilement " 51 | "effectue un zoom." 52 | 53 | #: bt-svg-viewer/js/bt-svg-viewer.js:411 54 | msgid "Drag to pan around the image." 55 | msgstr "Faites glisser pour vous déplacer dans l’image." 56 | 57 | msgid "Error: SVG preset not found for ID %s." 58 | msgstr "Erreur : préréglage SVG introuvable pour l’ID %s." 59 | 60 | msgid "Error: SVG source not specified. Use [btsvviewer src=\"path/to/file.svg\"]" 61 | msgstr "Erreur : source SVG non spécifiée. Utilisez [btsvviewer src=\"path/to/file.svg\"]" 62 | 63 | msgid "Error: Invalid SVG path." 64 | msgstr "Erreur : chemin SVG non valide." 65 | 66 | msgid "BT SVG Viewer Presets" 67 | msgstr "Préréglages BT SVG Viewer" 68 | 69 | msgid "BT SVG Viewer Preset" 70 | msgstr "Préréglage BT SVG Viewer" 71 | 72 | msgid "BT SVG Viewer" 73 | msgstr "BT SVG Viewer" 74 | 75 | msgid "Add New Preset" 76 | msgstr "Ajouter un préréglage" 77 | 78 | msgid "Add New BT SVG Viewer Preset" 79 | msgstr "Ajouter un préréglage BT SVG Viewer" 80 | 81 | msgid "Edit BT SVG Viewer Preset" 82 | msgstr "Modifier le préréglage BT SVG Viewer" 83 | 84 | msgid "New BT SVG Viewer Preset" 85 | msgstr "Nouveau préréglage BT SVG Viewer" 86 | 87 | msgid "View BT SVG Viewer Preset" 88 | msgstr "Voir le préréglage BT SVG Viewer" 89 | 90 | msgid "Search BT SVG Viewer Presets" 91 | msgstr "Rechercher des préréglages BT SVG Viewer" 92 | 93 | msgid "No presets found" 94 | msgstr "Aucun préréglage trouvé" 95 | 96 | msgid "No presets found in trash" 97 | msgstr "Aucun préréglage trouvé dans la corbeille" 98 | 99 | msgid "Please select an SVG before loading the preview." 100 | msgstr "Veuillez sélectionner un SVG avant de charger l’aperçu." 101 | 102 | msgid "Captured viewer state from the preview." 103 | msgstr "État du viewer capturé depuis l’aperçu." 104 | 105 | msgid "Unable to capture the current state. Refresh the preview and try again." 106 | msgstr "Impossible de capturer l’état actuel. Actualisez l’aperçu puis réessayez." 107 | 108 | msgid "Shortcode copied to clipboard." 109 | msgstr "Shortcode copié dans le presse-papiers." 110 | 111 | msgid "Press ⌘/Ctrl+C to copy the shortcode." 112 | msgstr "Appuyez sur ⌘/Ctrl+C pour copier le shortcode." 113 | 114 | msgid "BT SVG Viewer Settings" 115 | msgstr "Paramètres de BT SVG Viewer" 116 | 117 | msgid "Settings" 118 | msgstr "Paramètres" 119 | 120 | msgid "Help" 121 | msgstr "Aide" 122 | 123 | msgid "Preset Shortcode" 124 | msgstr "Shortcode du préréglage" 125 | 126 | msgid "Copy" 127 | msgstr "Copier" 128 | 129 | msgid "Use this shortcode in pages or posts to embed this preset." 130 | msgstr "Utilisez ce shortcode dans des pages ou des articles pour intégrer ce préréglage." 131 | 132 | msgid "SVG Source URL" 133 | msgstr "URL source du SVG" 134 | 135 | msgid "https://example.com/my-graphic.svg or uploads/2025/graphic.svg" 136 | msgstr "https://example.com/my-graphic.svg ou uploads/2025/graphic.svg" 137 | 138 | msgid "Select SVG" 139 | msgstr "Sélectionner un SVG" 140 | 141 | msgid "Viewer Height" 142 | msgstr "Hauteur du viewer" 143 | 144 | msgid "Min Zoom (%)" 145 | msgstr "Zoom minimum (%)" 146 | 147 | msgid "Max Zoom (%)" 148 | msgstr "Zoom maximum (%)" 149 | 150 | msgid "Initial Zoom (%)" 151 | msgstr "Zoom initial (%)" 152 | 153 | msgid "Zoom Increment (%)" 154 | msgstr "Incrément de zoom (%)" 155 | 156 | msgid "Center X" 157 | msgstr "Centre X" 158 | 159 | msgid "Center Y" 160 | msgstr "Centre Y" 161 | 162 | msgid "Controls Position" 163 | msgstr "Position des contrôles" 164 | 165 | msgid "Top" 166 | msgstr "Haut" 167 | 168 | msgid "Bottom" 169 | msgstr "Bas" 170 | 171 | msgid "Left" 172 | msgstr "Gauche" 173 | 174 | msgid "Right" 175 | msgstr "Droite" 176 | 177 | msgid "Controls Buttons/Layout" 178 | msgstr "Boutons/Disposition des contrôles" 179 | 180 | msgid "" 181 | "Combine multiple options with commas. Examples: both, icon, text, compact, " 182 | "labels-on-hover, minimal, alignleft, aligncenter, alignright, custom,both," 183 | "aligncenter,zoom_in,zoom_out,reset,center,coords" 184 | msgstr "" 185 | "Combinez plusieurs options séparées par des virgules. Exemples : both, icon, " 186 | "text, compact, labels-on-hover, minimal, alignleft, aligncenter, alignright, " 187 | "custom,both,aligncenter,zoom_in,zoom_out,reset,center,coords" 188 | 189 | msgid "Title (optional)" 190 | msgstr "Titre (optionnel)" 191 | 192 | msgid "Caption (optional)" 193 | msgstr "Légende (optionnelle)" 194 | 195 | msgid "Supports basic HTML formatting." 196 | msgstr "Prend en charge le HTML de base." 197 | 198 | msgid "Load / Refresh Preview" 199 | msgstr "Charger / Actualiser l’aperçu" 200 | 201 | msgid "Use Current View for Initial State" 202 | msgstr "Utiliser la vue actuelle comme état initial" 203 | 204 | msgid "" 205 | "Help content is not available. Run the \"Render Help\" build step to " 206 | "regenerate it." 207 | msgstr "" 208 | "Le contenu d’aide est indisponible. Exécutez l’étape de build « Render Help » " 209 | "pour le régénérer." 210 | 211 | msgid "Zoom In" 212 | msgstr "Zoom avant" 213 | 214 | msgid "Zoom In (Ctrl +)" 215 | msgstr "Zoom avant (Ctrl +)" 216 | 217 | msgid "Zoom Out" 218 | msgstr "Zoom arrière" 219 | 220 | msgid "Zoom Out (Ctrl -)" 221 | msgstr "Zoom arrière (Ctrl -)" 222 | 223 | msgid "Reset Zoom" 224 | msgstr "Réinitialiser le zoom" 225 | 226 | msgid "Center View" 227 | msgstr "Centrer la vue" 228 | 229 | msgid "Copy Center" 230 | msgstr "Copier le centre" 231 | 232 | msgid "Copy current center coordinates" 233 | msgstr "Copier les coordonnées du centre actuel" 234 | 235 | msgid "Shortcode" 236 | msgstr "Shortcode" 237 | 238 | -------------------------------------------------------------------------------- /bt-svg-viewer/languages/bt-svg-viewer-de_DE.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: BT SVG Viewer 1.0.1\n" 4 | "POT-Creation-Date: 2025-11-08 00:00+0000\n" 5 | "PO-Revision-Date: 2025-11-08 00:00+0000\n" 6 | "Last-Translator: \n" 7 | "Language-Team: German\n" 8 | "Language: de_DE\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 13 | "X-Generator: GPT-5 Codex\n" 14 | "X-Domain: bt-svg-viewer\n" 15 | 16 | #: bt-svg-viewer/admin/js/admin.js:35 17 | msgid "Full shortcode copied to clipboard." 18 | msgstr "Vollständiger Shortcode in die Zwischenablage kopiert." 19 | 20 | #: bt-svg-viewer/admin/js/admin.js:1297 21 | msgid "Copy full shortcode" 22 | msgstr "Vollständigen Shortcode kopieren" 23 | 24 | #: bt-svg-viewer/bt-svg-viewer-plugin.php:647 25 | msgid "Enable asset cache busting for debugging" 26 | msgstr "Asset-Cache-Busting für Debugging aktivieren" 27 | 28 | #: bt-svg-viewer/bt-svg-viewer-plugin.php:651 29 | msgid "" 30 | "Adds a unique suffix to script and style versions so browsers always fetch " 31 | "the latest assets. Useful for local testing; disable for production." 32 | msgstr "" 33 | "Fügt Skript- und Stilversionen ein eindeutiges Suffix hinzu, damit Browser " 34 | "immer die neuesten Assets laden. Nützlich für lokale Tests; für den " 35 | "Produktionsbetrieb deaktivieren." 36 | 37 | #: bt-svg-viewer/js/bt-svg-viewer.js:404 38 | msgid "Cmd/Ctrl-click to zoom in, Option/Alt-click to zoom out." 39 | msgstr "" 40 | "Mit Cmd/Strg-Klick vergrößern, mit Wahltaste/Alt-Klick verkleinern." 41 | 42 | #: bt-svg-viewer/js/bt-svg-viewer.js:406 43 | msgid "Scroll up to zoom in, scroll down to zoom out." 44 | msgstr "Nach oben scrollen zum Vergrößern, nach unten scrollen zum Verkleinern." 45 | 46 | #: bt-svg-viewer/js/bt-svg-viewer.js:409 47 | msgid "Drag to pan around the image while scrolling zooms." 48 | msgstr "Ziehen zum Schwenken, während scrollen zoomt." 49 | 50 | #: bt-svg-viewer/js/bt-svg-viewer.js:411 51 | msgid "Drag to pan around the image." 52 | msgstr "Ziehen zum Schwenken des Bildes." 53 | 54 | msgid "Error: SVG preset not found for ID %s." 55 | msgstr "Fehler: SVG-Voreinstellung wurde für die ID %s nicht gefunden." 56 | 57 | msgid "Error: SVG source not specified. Use [btsvviewer src=\"path/to/file.svg\"]" 58 | msgstr "Fehler: SVG-Quelle nicht angegeben. Verwenden Sie [btsvviewer src=\"path/to/file.svg\"]" 59 | 60 | msgid "Error: Invalid SVG path." 61 | msgstr "Fehler: Ungültiger SVG-Pfad." 62 | 63 | msgid "BT SVG Viewer Presets" 64 | msgstr "SVG-Viewer-Voreinstellungen" 65 | 66 | msgid "BT SVG Viewer Preset" 67 | msgstr "SVG-Viewer-Voreinstellung" 68 | 69 | msgid "BT SVG Viewer" 70 | msgstr "BT SVG Viewer" 71 | 72 | msgid "Add New Preset" 73 | msgstr "Neue Voreinstellung hinzufügen" 74 | 75 | msgid "Add New BT SVG Viewer Preset" 76 | msgstr "Neue SVG-Viewer-Voreinstellung hinzufügen" 77 | 78 | msgid "Edit BT SVG Viewer Preset" 79 | msgstr "SVG-Viewer-Voreinstellung bearbeiten" 80 | 81 | msgid "New BT SVG Viewer Preset" 82 | msgstr "Neue SVG-Viewer-Voreinstellung" 83 | 84 | msgid "View BT SVG Viewer Preset" 85 | msgstr "SVG-Viewer-Voreinstellung anzeigen" 86 | 87 | msgid "Search BT SVG Viewer Presets" 88 | msgstr "SVG-Viewer-Voreinstellungen durchsuchen" 89 | 90 | msgid "No presets found" 91 | msgstr "Keine Voreinstellungen gefunden" 92 | 93 | msgid "No presets found in trash" 94 | msgstr "Keine Voreinstellungen im Papierkorb gefunden" 95 | 96 | msgid "Please select an SVG before loading the preview." 97 | msgstr "Bitte wählen Sie ein SVG aus, bevor Sie die Vorschau laden." 98 | 99 | msgid "Captured viewer state from the preview." 100 | msgstr "Viewer-Status aus der Vorschau übernommen." 101 | 102 | msgid "Unable to capture the current state. Refresh the preview and try again." 103 | msgstr "Aktueller Status kann nicht erfasst werden. Aktualisieren Sie die Vorschau und versuchen Sie es erneut." 104 | 105 | msgid "Shortcode copied to clipboard." 106 | msgstr "Shortcode in die Zwischenablage kopiert." 107 | 108 | msgid "Press ⌘/Ctrl+C to copy the shortcode." 109 | msgstr "Drücken Sie ⌘/Strg+C, um den Shortcode zu kopieren." 110 | 111 | msgid "BT SVG Viewer Settings" 112 | msgstr "SVG-Viewer-Einstellungen" 113 | 114 | msgid "Settings" 115 | msgstr "Einstellungen" 116 | 117 | msgid "Help" 118 | msgstr "Hilfe" 119 | 120 | msgid "Preset Shortcode" 121 | msgstr "Shortcode der Voreinstellung" 122 | 123 | msgid "Copy" 124 | msgstr "Kopieren" 125 | 126 | msgid "Use this shortcode in pages or posts to embed this preset." 127 | msgstr "Verwenden Sie diesen Shortcode in Seiten oder Beiträgen, um diese Voreinstellung einzubetten." 128 | 129 | msgid "SVG Source URL" 130 | msgstr "SVG-Quell-URL" 131 | 132 | msgid "https://example.com/my-graphic.svg or uploads/2025/graphic.svg" 133 | msgstr "https://example.com/my-graphic.svg oder uploads/2025/graphic.svg" 134 | 135 | msgid "Select SVG" 136 | msgstr "SVG auswählen" 137 | 138 | msgid "Viewer Height" 139 | msgstr "Viewer-Höhe" 140 | 141 | msgid "Min Zoom (%)" 142 | msgstr "Minimaler Zoom (%)" 143 | 144 | msgid "Max Zoom (%)" 145 | msgstr "Maximaler Zoom (%)" 146 | 147 | msgid "Initial Zoom (%)" 148 | msgstr "Initialer Zoom (%)" 149 | 150 | msgid "Zoom Increment (%)" 151 | msgstr "Zoomschritt (%)" 152 | 153 | msgid "Center X" 154 | msgstr "Zentrum X" 155 | 156 | msgid "Center Y" 157 | msgstr "Zentrum Y" 158 | 159 | msgid "Controls Position" 160 | msgstr "Position der Bedienelemente" 161 | 162 | msgid "Top" 163 | msgstr "Oben" 164 | 165 | msgid "Bottom" 166 | msgstr "Unten" 167 | 168 | msgid "Left" 169 | msgstr "Links" 170 | 171 | msgid "Right" 172 | msgstr "Rechts" 173 | 174 | msgid "Controls Buttons/Layout" 175 | msgstr "Schaltflächen/Layout der Bedienelemente" 176 | 177 | msgid "" 178 | "Combine multiple options with commas. Examples: both, icon, text, compact, " 179 | "labels-on-hover, minimal, alignleft, aligncenter, alignright, custom,both," 180 | "aligncenter,zoom_in,zoom_out,reset,center,coords" 181 | msgstr "" 182 | "Kombinieren Sie mehrere Optionen mit Kommas. Beispiele: both, icon, text, " 183 | "compact, labels-on-hover, minimal, alignleft, aligncenter, alignright, " 184 | "custom,both,aligncenter,zoom_in,zoom_out,reset,center,coords" 185 | 186 | msgid "Title (optional)" 187 | msgstr "Titel (optional)" 188 | 189 | msgid "Caption (optional)" 190 | msgstr "Beschriftung (optional)" 191 | 192 | msgid "Supports basic HTML formatting." 193 | msgstr "Unterstützt grundlegende HTML-Formatierung." 194 | 195 | msgid "Load / Refresh Preview" 196 | msgstr "Vorschau laden/aktualisieren" 197 | 198 | msgid "Use Current View for Initial State" 199 | msgstr "Aktuelle Ansicht als Ausgangszustand verwenden" 200 | 201 | msgid "" 202 | "Help content is not available. Run the \"Render Help\" build step to " 203 | "regenerate it." 204 | msgstr "" 205 | "Hilfsinhalt ist nicht verfügbar. Führen Sie den Build-Schritt „Render Help“ " 206 | "aus, um ihn neu zu erzeugen." 207 | 208 | msgid "Zoom In" 209 | msgstr "Vergrößern" 210 | 211 | msgid "Zoom In (Ctrl +)" 212 | msgstr "Vergrößern (Strg +)" 213 | 214 | msgid "Zoom Out" 215 | msgstr "Verkleinern" 216 | 217 | msgid "Zoom Out (Ctrl -)" 218 | msgstr "Verkleinern (Strg -)" 219 | 220 | msgid "Reset Zoom" 221 | msgstr "Zoom zurücksetzen" 222 | 223 | msgid "Center View" 224 | msgstr "Ansicht zentrieren" 225 | 226 | msgid "Copy Center" 227 | msgstr "Zentrum kopieren" 228 | 229 | msgid "Copy current center coordinates" 230 | msgstr "Aktuelle Mittelpunktkoordinaten kopieren" 231 | 232 | msgid "Shortcode" 233 | msgstr "Shortcode" 234 | 235 | -------------------------------------------------------------------------------- /bt-svg-viewer/admin/changelog.html: -------------------------------------------------------------------------------- 1 |

1.0.21

2 | 3 |

2025–11–28 04:25

4 | 5 |

1.0.20

6 | 7 |

2025–11–28 04:17

8 | 9 |

CHANGED

10 | 11 |
    12 |
  • Rename shortcode hooks, options, post types, and meta keys to the btsvviewer prefix with automatic migration for legacy data.
  • 13 |
  • Continue honoring existing [svg_viewer] embeds by registering the legacy shortcode and admin-post handlers alongside the new ones.
  • 14 |
15 | 16 |

IMPROVED

17 | 18 |
    19 |
  • Scope localized JS globals (btsvviewerConfig, btsvviewerAdmin, window.btsvviewerInstances) to avoid conflicts with other plugins.
  • 20 |
  • Embed the resolved SVG URL in markup data attributes so presets and tests can read it without extra JavaScript.
  • 21 |
22 | 23 |

FIXED

24 | 25 |
    26 |
  • Align unit tests and runtime assets with the renamed identifiers to keep the suite green.
  • 27 |
28 | 29 |

1.0.19

30 | 31 |

2025–11–19 10:40

32 | 33 |

CHANGED

34 | 35 |
    36 |
  • Changed class name to use plugin prefix
  • 37 |
38 | 39 |

1.0.18

40 | 41 |

2025–11–19 10:39

42 | 43 |

CHANGED

44 | 45 |
    46 |
  • Changed class name to use plugin prefix
  • 47 |
48 | 49 |

1.0.17

50 | 51 |

2025–11–19 10:28

52 | 53 |

1.0.16

54 | 55 |

2025–11–19 10:22

56 | 57 |

CHANGED

58 | 59 |
    60 |
  • Prefix the main PHP surface (BTSVVI_Viewer, option keys, hooks) to avoid naming collisions.
  • 61 |
62 | 63 |

IMPROVED

64 | 65 |
    66 |
  • More extensive test suite
  • 67 |
  • Initialize viewer instances via enqueued scripts and a JS queue to remove inline JS.
  • 68 |
69 | 70 |

FIXED

71 | 72 |
    73 |
  • Sanitize and validate all defaults and preset inputs before saving.
  • 74 |
  • Block direct file access on the bootstrap path and harden cached admin markup handling.
  • 75 |
76 | 77 |

1.0.14

78 | 79 |

2025–11–10 07:44

80 | 81 |

CHANGED

82 | 83 |
    84 |
  • Rename plugin and all text domains to BT SVG Viewer
  • 85 |
86 | 87 |

IMPROVED

88 | 89 |
    90 |
  • Add screenshots for Plugin repository display
  • 91 |
92 | 93 |

1.0.13

94 | 95 |

2025–11–10 05:24

96 | 97 |

FIXED

98 | 99 |
    100 |
  • Fix readme.txt for plugin submission
  • 101 |
102 | 103 |

1.0.12

104 | 105 |

2025–11–10 04:59

106 | 107 |

FIXED

108 | 109 |
    110 |
  • Rename without WP for plugin submission
  • 111 |
112 | 113 |

1.0.10

114 | 115 |

2025–11–09 13:24

116 | 117 |

IMPROVED

118 | 119 |
    120 |
  • Add plugins page settings link
  • 121 |
122 | 123 |

1.0.9

124 | 125 |

2025–11–09 11:20

126 | 127 |

FIXED

128 | 129 |
    130 |
  • Broken icon/button text after plugin submission fixes
  • 131 |
132 | 133 |

1.0.8

134 | 135 |

2025–11–09 10:33

136 | 137 |

1.0.7

138 | 139 |

2025–11–09 10:28

140 | 141 |

NEW

142 | 143 |
    144 |
  • Document shortcode aliases for button colors
  • 145 |
  • Provide example shortcode using button_bg/button_fg aliases
  • 146 |
  • Pan:drag and zoom:scroll behaviors
  • 147 |
  • Pan/zoom behavior shortcode options and gesture captions
  • 148 |
  • Update plugin readme highlights and upgrade notice for animated zoom release
  • 149 |
150 | 151 |

IMPROVED

152 | 153 |
    154 |
  • Note preset color pickers for fill/border/foreground
  • 155 |
  • Add styling guidance for CSS custom properties
  • 156 |
  • Lock zoom to coordinates to prevent drift
  • 157 |
  • Smooth transition for zoom and pan
  • 158 |
  • Cache-busting toggle and smooth zoom behaviour in project docs
  • 159 |
  • Add new options to localizations
  • 160 |
  • Disable zoom in/out buttons when at min/max zoom
  • 161 |
  • Push center coordinates to live preview so center button works immediately after saving zoom/position coordinates
  • 162 |
  • Admin auto-centers newly selected SVGs for easier tweaking
  • 163 |
  • Zoom controls snap to min/max and clearly show disabled states
  • 164 |
165 | 166 |

FIXED

167 | 168 |
    169 |
  • Captured center values now match the Center View preview
  • 170 |
  • Preset pan mode overrides persist when switching between scroll and drag
  • 171 |
172 | 173 |

1.1.0

174 | 175 |

2025–11–09 14:10

176 | 177 |

NEW

178 | 179 |
    180 |
  • Pan and zoom interaction modes configurable via shortcode/preset, with automatic gesture captions.
  • 181 |
  • Smooth, cursor-focused zoom animations for wheel, slider, and modifier-click gestures.
  • 182 |
  • Defaults tab option to enable asset cache busting for debugging (auto-enabled on dev.* / wptest.* domains).
  • 183 |
184 | 185 |

IMPROVED

186 | 187 |
    188 |
  • Drag panning now tracks 1:1 with the pointer and ignores stray wheel input.
  • 189 |
  • Zooming keeps the focus point locked under the cursor while preventing unintended panning.
  • 190 |
191 | 192 |

1.0.6

193 | 194 |

2025–11–08 10:35

195 | 196 |

FIXED

197 | 198 |
    199 |
  • CSS caching not keeping up with versions
  • 200 |
201 | 202 |

1.0.5

203 | 204 |

2025–11–08 10:13

205 | 206 |

FIXED

207 | 208 |
    209 |
  • Bad permissions in zip distro
  • 210 |
211 | 212 |

1.0.4

213 | 214 |

2025–11–08 08:54

215 | 216 |

FIXED

217 | 218 |
    219 |
  • Fixing deploy process
  • 220 |
221 | 222 |

1.0.3

223 | 224 |

2025–11–08 08:47

225 | 226 |

FIXED

227 | 228 |
    229 |
  • Changelog duplication
  • 230 |
231 | 232 |

1.0.2

233 | 234 |

2025–11–08 08:39

235 | 236 |

NEW

237 | 238 |
    239 |
  • DE, ES, FR, IT localizations
  • 240 |
  • Help and Changelog tabs in admin panel
  • 241 |
242 | 243 |

IMPROVED

244 | 245 |
    246 |
  • Major README overhaul
  • 247 |
248 | 249 |

1.0.1

250 | 251 |

2025–11–08 06:40

252 | 253 |

NEW

254 | 255 |
    256 |
  • Admin panel where you can create custom SVG posts with all configuration options stored
  • 257 |
  • Buttons can be configured to left, right, top, bottom
  • 258 |
  • Buttons can be configured to icon, text, or both
  • 259 |
  • Buttons can be configured with custom settings to display only certain buttons, define left/right alignment, and more
  • 260 |
  • Preview in admin panel shows how buttons will appear
  • 261 |
  • Preview in admin panel can be panned/zoomed and then its current state can be saved as the initial state for the front-end display
  • 262 |
263 | 264 |

BT SVG Viewer 1.0.0

265 | 266 |

2025–11–07 08:00

267 | 268 |

NEW

269 | 270 |
    271 |
  • Initial release
  • 272 |
  • Shortcode for creating an SVG viewer with zoom and pan
  • 273 |
274 | -------------------------------------------------------------------------------- /tests/js/bt-svg-viewer.test.js: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; 2 | import SVGViewer from "../../bt-svg-viewer/js/bt-svg-viewer.js"; 3 | 4 | const flush = () => new Promise((resolve) => setTimeout(resolve, 0)); 5 | 6 | describe("SVGViewer core behavior", () => { 7 | const setupDom = (options = {}) => { 8 | const { includeSlider = false } = options; 9 | document.body.innerHTML = ` 10 |
11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | ${ 20 | includeSlider 21 | ? '' 22 | : "" 23 | } 24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | `; 32 | }; 33 | 34 | const createViewer = (overrides = {}) => 35 | new SVGViewer({ 36 | viewerId: "viewer-1", 37 | svgUrl: "https://example.com/test.svg", 38 | initialZoom: 1, 39 | minZoom: 0.5, 40 | maxZoom: 4, 41 | zoomStep: 0.25, 42 | showCoordinates: true, 43 | panMode: "drag", 44 | zoomMode: "scroll", 45 | ...overrides, 46 | }); 47 | 48 | beforeEach(() => { 49 | setupDom(); 50 | vi.spyOn(window, "requestAnimationFrame").mockImplementation((cb) => { 51 | return setTimeout(() => cb(Date.now()), 0); 52 | }); 53 | vi.spyOn(window, "cancelAnimationFrame").mockImplementation((id) => { 54 | clearTimeout(id); 55 | }); 56 | }); 57 | 58 | afterEach(() => { 59 | vi.restoreAllMocks(); 60 | document.body.innerHTML = ""; 61 | }); 62 | 63 | it("normalizes interaction modes", () => { 64 | expect(SVGViewer.normalizePanMode("Drag")).toBe("drag"); 65 | expect(SVGViewer.normalizePanMode("scroll")).toBe("scroll"); 66 | expect(SVGViewer.normalizeZoomMode("CLICK")).toBe("click"); 67 | expect(SVGViewer.normalizeZoomMode("super scroll")).toBe("super_scroll"); 68 | }); 69 | 70 | it("initializes with provided options and loads SVG", async () => { 71 | const viewer = createViewer({ showCoordinates: false }); 72 | 73 | await flush(); 74 | 75 | expect(global.fetch).toHaveBeenCalledWith("https://example.com/test.svg"); 76 | expect(viewer.currentZoom).toBe(1); 77 | expect( 78 | document 79 | .querySelector('[data-viewer="viewer-1"].zoom-percentage') 80 | .textContent.trim() 81 | ).toContain("100"); 82 | }); 83 | 84 | it("clamps zoom values when setZoom is called directly", () => { 85 | const viewer = createViewer({ 86 | maxZoom: 1.5, 87 | minZoom: 0.5, 88 | zoomStep: 0.5, 89 | showCoordinates: false, 90 | }); 91 | 92 | viewer.setZoom(1.5, { animate: false }); 93 | expect(viewer.currentZoom).toBeCloseTo(1.5, 5); 94 | 95 | viewer.setZoom(2.0, { animate: false }); 96 | expect(viewer.currentZoom).toBeCloseTo(1.5, 5); 97 | 98 | viewer.setZoom(0.5, { animate: false }); 99 | expect(viewer.currentZoom).toBeCloseTo(0.5, 5); 100 | 101 | viewer.setZoom(0.2, { animate: false }); 102 | expect(viewer.currentZoom).toBeCloseTo(0.5, 5); 103 | }); 104 | 105 | it("propagates slider input to setZoom", async () => { 106 | setupDom({ includeSlider: true }); 107 | 108 | const viewer = createViewer({ 109 | minZoom: 1, 110 | maxZoom: 2, 111 | zoomStep: 0.1, 112 | showCoordinates: false, 113 | panMode: "scroll", 114 | zoomMode: "super_scroll", 115 | }); 116 | 117 | const slider = viewer.zoomSliderEls[0]; 118 | expect(slider).toBeDefined(); 119 | 120 | slider.value = "150"; 121 | const setZoomSpy = vi.spyOn(viewer, "setZoom"); 122 | slider.dispatchEvent(new Event("input")); 123 | 124 | await flush(); 125 | 126 | expect(setZoomSpy).toHaveBeenLastCalledWith(1.5); 127 | }); 128 | 129 | it("copies center coordinates to clipboard when available", async () => { 130 | const viewer = createViewer({ 131 | showCoordinates: true, 132 | panMode: "scroll", 133 | zoomMode: "super_scroll", 134 | }); 135 | 136 | const copyButton = document.querySelector( 137 | '[data-viewer="viewer-1"].coord-copy-btn' 138 | ); 139 | viewer.getVisibleCenterPoint = vi 140 | .fn() 141 | .mockReturnValue({ x: 12.345, y: 67.89 }); 142 | 143 | copyButton.click(); 144 | await flush(); 145 | 146 | expect(navigator.clipboard.writeText).toHaveBeenCalledWith("12.35, 67.89"); 147 | }); 148 | 149 | it("falls back to prompt when clipboard API is unavailable", async () => { 150 | const viewer = createViewer({ 151 | showCoordinates: true, 152 | panMode: "scroll", 153 | zoomMode: "super_scroll", 154 | }); 155 | 156 | const copyButton = document.querySelector( 157 | '[data-viewer="viewer-1"].coord-copy-btn' 158 | ); 159 | viewer.getVisibleCenterPoint = vi.fn().mockReturnValue({ x: 1.2, y: 3.4 }); 160 | 161 | const originalWriteText = navigator.clipboard.writeText; 162 | navigator.clipboard.writeText = undefined; 163 | window.prompt.mockClear(); 164 | 165 | copyButton.click(); 166 | await flush(); 167 | 168 | expect(window.prompt).toHaveBeenCalledWith( 169 | "Copy coordinates", 170 | "1.20, 3.40" 171 | ); 172 | 173 | navigator.clipboard.writeText = originalWriteText; 174 | }); 175 | 176 | it("invokes performWheelZoom for eligible wheel events", async () => { 177 | const viewer = createViewer({ 178 | maxZoom: 3, 179 | panMode: "scroll", 180 | zoomMode: "scroll", 181 | showCoordinates: false, 182 | }); 183 | 184 | const container = document.querySelector( 185 | '[data-viewer="viewer-1"].svg-container' 186 | ); 187 | viewer.baseDimensions = { width: 2000, height: 1000 }; 188 | viewer.baseOrigin = { x: 0, y: 0 }; 189 | viewer.unitsPerCss = { x: 1, y: 1 }; 190 | Object.defineProperty(container, "clientWidth", { value: 500 }); 191 | Object.defineProperty(container, "clientHeight", { value: 400 }); 192 | Object.defineProperty(container, "scrollWidth", { value: 600 }); 193 | Object.defineProperty(container, "scrollHeight", { value: 800 }); 194 | 195 | const performWheelZoomSpy = vi.spyOn(viewer, "performWheelZoom"); 196 | 197 | const wheelEvent = new WheelEvent("wheel", { 198 | deltaY: -160, 199 | clientX: 250, 200 | clientY: 200, 201 | bubbles: true, 202 | cancelable: true, 203 | }); 204 | 205 | viewer.handleMouseWheel(wheelEvent); 206 | await flush(); 207 | 208 | expect(performWheelZoomSpy).toHaveBeenCalled(); 209 | }); 210 | 211 | it("adjusts container scroll when zooming around a focus point", () => { 212 | const viewer = createViewer({ 213 | maxZoom: 3, 214 | panMode: "scroll", 215 | zoomMode: "scroll", 216 | showCoordinates: false, 217 | }); 218 | 219 | const container = document.querySelector( 220 | '[data-viewer="viewer-1"].svg-container' 221 | ); 222 | viewer.baseDimensions = { width: 2000, height: 1000 }; 223 | viewer.baseOrigin = { x: 0, y: 0 }; 224 | viewer.unitsPerCss = { x: 1, y: 1 }; 225 | Object.defineProperty(container, "clientWidth", { value: 500 }); 226 | Object.defineProperty(container, "clientHeight", { value: 400 }); 227 | Object.defineProperty(container, "scrollWidth", { 228 | get() { 229 | return 800; 230 | }, 231 | }); 232 | Object.defineProperty(container, "scrollHeight", { 233 | get() { 234 | return 900; 235 | }, 236 | }); 237 | 238 | viewer.setZoom(1.5, { 239 | animate: false, 240 | focusX: 400, 241 | focusY: 300, 242 | focusOffsetX: 200, 243 | focusOffsetY: 150, 244 | }); 245 | 246 | expect(container.scrollLeft).toBeGreaterThanOrEqual(0); 247 | expect(container.scrollTop).toBeGreaterThanOrEqual(0); 248 | }); 249 | 250 | it("disables zoom buttons when reaching bounds", async () => { 251 | const viewer = createViewer({ 252 | maxZoom: 1.5, 253 | minZoom: 0.5, 254 | zoomStep: 0.5, 255 | showCoordinates: false, 256 | }); 257 | 258 | const zoomInButton = document.querySelector( 259 | '[data-viewer="viewer-1"].zoom-in-btn' 260 | ); 261 | const zoomOutButton = document.querySelector( 262 | '[data-viewer="viewer-1"].zoom-out-btn' 263 | ); 264 | 265 | viewer.setZoom(1.5, { animate: false }); 266 | await flush(); 267 | 268 | expect(zoomInButton.disabled).toBe(true); 269 | expect(zoomInButton.getAttribute("aria-disabled")).toBe("true"); 270 | expect(zoomOutButton.disabled).toBe(false); 271 | 272 | viewer.setZoom(0.5, { animate: false }); 273 | await flush(); 274 | 275 | expect(zoomOutButton.disabled).toBe(true); 276 | expect(zoomOutButton.getAttribute("aria-disabled")).toBe("true"); 277 | expect(zoomInButton.disabled).toBe(false); 278 | }); 279 | 280 | it("enforces zoom bounds through zoom buttons", async () => { 281 | const viewer = createViewer({ 282 | maxZoom: 1.5, 283 | minZoom: 0.5, 284 | zoomStep: 0.5, 285 | showCoordinates: false, 286 | }); 287 | 288 | viewer.setZoom(viewer.computeZoomTarget("in"), { animate: false }); 289 | expect(viewer.currentZoom).toBeCloseTo(1.5, 5); 290 | 291 | viewer.setZoom(viewer.computeZoomTarget("out"), { animate: false }); 292 | expect(viewer.currentZoom).toBeCloseTo(1.0, 5); 293 | 294 | viewer.setZoom(viewer.computeZoomTarget("out"), { animate: false }); 295 | expect(viewer.currentZoom).toBeCloseTo(0.5, 5); 296 | }); 297 | }); 298 | -------------------------------------------------------------------------------- /tests/test-plugin.php: -------------------------------------------------------------------------------- 1 | register_preset_post_type(); 27 | 28 | if (!function_exists('sanitize_hex_color')) { 29 | require_once ABSPATH . WPINC . '/formatting.php'; 30 | 31 | if (!function_exists('sanitize_hex_color')) { 32 | function sanitize_hex_color($color) 33 | { 34 | $color = is_string($color) ? trim($color) : ''; 35 | 36 | if ($color === '') { 37 | return false; 38 | } 39 | 40 | if ($color[0] !== '#') { 41 | $color = '#' . $color; 42 | } 43 | 44 | if (preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $color)) { 45 | return strtolower($color); 46 | } 47 | 48 | return false; 49 | } 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * Inline shortcode attributes should override preset values. 56 | */ 57 | public function test_shortcode_inline_overrides_preset_values() 58 | { 59 | $uploads_url = 'https://example.com/uploads'; 60 | 61 | $uploads_filter = static function ($dirs) use ($uploads_url) { 62 | $dirs['baseurl'] = $uploads_url; 63 | return $dirs; 64 | }; 65 | add_filter('upload_dir', $uploads_filter); 66 | 67 | $preset_id = self::factory()->post->create( 68 | array( 69 | 'post_type' => 'btsvviewer_preset', 70 | 'post_status' => 'publish', 71 | 'post_title' => 'Override Test Preset', 72 | ) 73 | ); 74 | 75 | update_post_meta($preset_id, '_btsvviewer_src', 'preset-image.svg'); 76 | update_post_meta($preset_id, '_btsvviewer_button_fill', '#123456'); 77 | 78 | $output = self::$plugin->render_shortcode( 79 | array( 80 | 'id' => $preset_id, 81 | 'button_fill' => '#abcdef', 82 | 'height' => '720px', 83 | ) 84 | ); 85 | 86 | $this->assertStringContainsString('--bt-svg-viewer-button-fill: #abcdef', $output); 87 | $this->assertStringContainsString('style="height: 720px"', $output); 88 | 89 | remove_filter('upload_dir', $uploads_filter); 90 | } 91 | 92 | /** 93 | * Missing preset IDs should return an error message. 94 | */ 95 | public function test_render_shortcode_returns_error_when_preset_missing() 96 | { 97 | $output = self::$plugin->render_shortcode(array('id' => 999999)); 98 | 99 | $this->assertStringContainsString( 100 | 'Error: SVG preset not found', 101 | wp_strip_all_tags($output) 102 | ); 103 | } 104 | 105 | /** 106 | * Helper should sanitize color declarations and infer defaults. 107 | */ 108 | public function test_get_button_color_style_declarations_sanitizes_values() 109 | { 110 | $method = new ReflectionMethod(BT_SVG_Viewer::class, 'get_button_color_style_declarations'); 111 | $method->setAccessible(true); 112 | 113 | $declarations = $method->invoke(self::$plugin, '#AaCcDd', '', ' #F0F0F0 '); 114 | 115 | $this->assertContains('--bt-svg-viewer-button-fill: #aaccdd', $declarations); 116 | $this->assertContains('--bt-svg-viewer-button-border: #aaccdd', $declarations); 117 | $this->assertContains('--bt-svg-viewer-button-text: #f0f0f0', $declarations); 118 | } 119 | 120 | /** 121 | * adjust_color_brightness should clamp values inside 0-255 and handle extremes. 122 | */ 123 | public function test_adjust_color_brightness_handles_extremes() 124 | { 125 | $method = new ReflectionMethod(BT_SVG_Viewer::class, 'adjust_color_brightness'); 126 | $method->setAccessible(true); 127 | 128 | $this->assertSame('#ffffff', $method->invoke(self::$plugin, '#ffffff', 15.0)); 129 | $this->assertSame('#000000', $method->invoke(self::$plugin, '#000000', -20.0)); 130 | $this->assertSame('#0d0d0d', $method->invoke(self::$plugin, '#000000', 5.0)); 131 | } 132 | 133 | /** 134 | * build_style_attribute should trim duplicates and semicolons. 135 | */ 136 | public function test_build_style_attribute_trims_duplicates() 137 | { 138 | $method = new ReflectionMethod(BT_SVG_Viewer::class, 'build_style_attribute'); 139 | $method->setAccessible(true); 140 | 141 | $result = $method->invoke( 142 | self::$plugin, 143 | array( 144 | ' color: red; ', 145 | 'color: red', 146 | 'background: #fff;;', 147 | '', 148 | ) 149 | ); 150 | 151 | $this->assertSame('color: red; background: #fff', $result); 152 | } 153 | 154 | /** 155 | * Ensure the plugin adds SVG support to the mime type list. 156 | */ 157 | public function test_btsvviewer_mime_type_is_added() 158 | { 159 | $existing = array( 160 | 'jpg' => 'image/jpeg', 161 | ); 162 | 163 | $result = self::$plugin->svg_use_mimetypes($existing); 164 | 165 | $this->assertArrayHasKey('svg', $result); 166 | $this->assertSame('image/svg+xml', $result['svg']); 167 | } 168 | 169 | /** 170 | * Rendering without a source should return a descriptive error message. 171 | */ 172 | public function test_render_shortcode_requires_source() 173 | { 174 | $output = self::$plugin->render_shortcode(array()); 175 | 176 | $this->assertStringContainsString( 177 | 'Error: SVG source not specified.', 178 | wp_strip_all_tags($output) 179 | ); 180 | } 181 | 182 | /** 183 | * Rendering with button color aliases should normalize and sanitize styles. 184 | */ 185 | public function test_render_shortcode_generates_button_styles_from_aliases() 186 | { 187 | $output = self::$plugin->render_shortcode( 188 | array( 189 | 'src' => 'https://example.com/test.svg', 190 | 'button_bg' => '#336699', 191 | 'button_border' => 'not-a-color', 192 | 'button_foreground' => '#ffffff', 193 | ) 194 | ); 195 | 196 | $this->assertStringContainsString('--bt-svg-viewer-button-fill: #336699', $output); 197 | $this->assertStringContainsString('--bt-svg-viewer-button-border: #336699', $output); 198 | $this->assertStringContainsString('--bt-svg-viewer-button-text: #ffffff', $output); 199 | } 200 | 201 | /** 202 | * Interaction configuration should resolve conflicting pan/zoom modes and expose helper messages. 203 | */ 204 | public function test_resolve_interaction_config_adjusts_pan_mode_and_messages() 205 | { 206 | $method = new ReflectionMethod(BT_SVG_Viewer::class, 'resolve_interaction_config'); 207 | $method->setAccessible(true); 208 | 209 | $result = $method->invoke(self::$plugin, 'scroll', 'scroll'); 210 | 211 | $this->assertSame('drag', $result['pan_mode']); 212 | $this->assertSame('scroll', $result['zoom_mode']); 213 | $this->assertContains( 214 | 'Scroll up to zoom in, scroll down to zoom out.', 215 | $result['messages'] 216 | ); 217 | $this->assertContains( 218 | 'Drag to pan around the image while scrolling zooms.', 219 | $result['messages'] 220 | ); 221 | } 222 | 223 | /** 224 | * Shortcode rendering should pick up preset data when provided via ID. 225 | */ 226 | public function test_render_shortcode_uses_preset_values() 227 | { 228 | $uploads_url = 'https://example.com/uploads'; 229 | 230 | $uploads_filter = static function ($dirs) use ($uploads_url) { 231 | $dirs['baseurl'] = $uploads_url; 232 | return $dirs; 233 | }; 234 | add_filter('upload_dir', $uploads_filter); 235 | 236 | $preset_id = self::factory()->post->create( 237 | array( 238 | 'post_type' => 'btsvviewer_preset', 239 | 'post_status' => 'publish', 240 | 'post_title' => 'Preset', 241 | ) 242 | ); 243 | 244 | update_post_meta($preset_id, '_btsvviewer_src', 'preset-path.svg'); 245 | update_post_meta($preset_id, '_btsvviewer_initial_zoom', 200); 246 | update_post_meta($preset_id, '_btsvviewer_pan_mode', 'drag'); 247 | update_post_meta($preset_id, '_btsvviewer_zoom_mode', 'click'); 248 | 249 | $output = self::$plugin->render_shortcode( 250 | array( 251 | 'id' => $preset_id, 252 | ) 253 | ); 254 | 255 | $this->assertStringContainsString('bt-svg-viewer-wrapper', $output); 256 | $this->assertStringContainsString('bt-svg-viewer-main', $output); 257 | $this->assertStringContainsString($uploads_url . '/preset-path.svg', $output); 258 | $this->assertStringContainsString('controls-mode', $output); 259 | 260 | remove_filter('upload_dir', $uploads_filter); 261 | } 262 | 263 | /** 264 | * Rendering through do_shortcode should enqueue frontend assets. 265 | */ 266 | public function test_shortcode_enqueue_assets() 267 | { 268 | if (wp_script_is('bt-svg-viewer-script')) { 269 | wp_dequeue_script('bt-svg-viewer-script'); 270 | } 271 | if (wp_style_is('bt-svg-viewer-style')) { 272 | wp_dequeue_style('bt-svg-viewer-style'); 273 | } 274 | 275 | $this->assertFalse(wp_style_is('bt-svg-viewer-style', 'enqueued')); 276 | $this->assertFalse(wp_script_is('bt-svg-viewer-script', 'enqueued')); 277 | 278 | do_action('wp_enqueue_scripts'); 279 | 280 | $this->assertTrue(wp_style_is('bt-svg-viewer-style', 'enqueued')); 281 | } 282 | 283 | } -------------------------------------------------------------------------------- /bt-svg-viewer/css/bt-svg-viewer.css: -------------------------------------------------------------------------------- 1 | /* BT SVG Viewer Plugin Styles */ 2 | 3 | .bt-svg-viewer-wrapper { 4 | display: flex; 5 | flex-direction: column; 6 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, 7 | Ubuntu, Cantarell, sans-serif; 8 | border: 1px solid #ddd; 9 | border-radius: 4px; 10 | overflow: hidden; 11 | background-color: #fff; 12 | --bt-svg-viewer-button-fill: #0073aa; 13 | --bt-svg-viewer-button-hover: #005a87; 14 | --bt-svg-viewer-button-border: #0073aa; 15 | --bt-svg-viewer-button-text: #fff; 16 | } 17 | 18 | .bt-svg-viewer-main { 19 | display: flex; 20 | flex-direction: column; 21 | gap: 0; 22 | } 23 | 24 | .bt-svg-viewer-main.controls-position-bottom { 25 | flex-direction: column-reverse; 26 | } 27 | 28 | .bt-svg-viewer-main.controls-position-left { 29 | flex-direction: row; 30 | } 31 | 32 | .bt-svg-viewer-main.controls-position-right { 33 | flex-direction: row-reverse; 34 | } 35 | 36 | .bt-svg-viewer-main.controls-position-left, 37 | .bt-svg-viewer-main.controls-position-right { 38 | align-items: stretch; 39 | } 40 | 41 | .bt-svg-viewer-main.controls-position-left .svg-controls, 42 | .bt-svg-viewer-main.controls-position-right .svg-controls { 43 | flex: 0 0 auto; 44 | } 45 | 46 | .bt-svg-viewer-main > .svg-container { 47 | flex: 1 1 auto; 48 | } 49 | 50 | .bt-svg-viewer-title, 51 | .bt-svg-viewer-caption { 52 | text-align: center; 53 | font-weight: 600; 54 | margin: 0; 55 | padding: 12px 16px; 56 | } 57 | 58 | .bt-svg-viewer-interaction-caption { 59 | font-weight: 400; 60 | font-size: 0.9em; 61 | color: #555; 62 | background-color: #fafafa; 63 | } 64 | 65 | .bt-svg-viewer-title { 66 | border-bottom: 1px solid #ddd; 67 | } 68 | 69 | .svg-controls { 70 | display: flex; 71 | gap: 10px; 72 | padding: 15px; 73 | background-color: #f5f5f5; 74 | border-bottom: 1px solid #ddd; 75 | flex-wrap: wrap; 76 | align-items: center; 77 | justify-content: flex-start; 78 | } 79 | 80 | .bt-svg-viewer-main.controls-position-bottom .svg-controls { 81 | border-top: 1px solid #ddd; 82 | border-bottom: none; 83 | } 84 | 85 | .bt-svg-viewer-main.controls-position-left .svg-controls, 86 | .bt-svg-viewer-main.controls-position-right .svg-controls { 87 | border-bottom: none; 88 | align-items: flex-start; 89 | justify-content: center; 90 | } 91 | 92 | .bt-svg-viewer-main.controls-position-left.controls-align-alignleft .svg-controls, 93 | .bt-svg-viewer-main.controls-position-right.controls-align-alignleft 94 | .svg-controls { 95 | align-items: flex-start; 96 | } 97 | 98 | .bt-svg-viewer-main.controls-position-left.controls-align-alignright .svg-controls, 99 | .bt-svg-viewer-main.controls-position-right.controls-align-alignright 100 | .svg-controls { 101 | align-items: flex-end; 102 | } 103 | 104 | .bt-svg-viewer-main.controls-position-left.controls-align-aligncenter 105 | .svg-controls, 106 | .bt-svg-viewer-main.controls-position-right.controls-align-aligncenter 107 | .svg-controls { 108 | align-items: center; 109 | } 110 | 111 | .bt-svg-viewer-main.controls-position-left .svg-controls { 112 | border-right: 1px solid #ddd; 113 | } 114 | 115 | .bt-svg-viewer-main.controls-position-right .svg-controls { 116 | border-left: 1px solid #ddd; 117 | } 118 | 119 | .svg-controls.controls-vertical { 120 | flex-direction: column; 121 | gap: 12px; 122 | align-items: stretch; 123 | } 124 | 125 | .svg-controls .zoom-slider-wrapper { 126 | display: inline-flex; 127 | align-items: center; 128 | width: var(--bt-svg-viewer-slider-width, 240px); 129 | padding: 0 4px; 130 | } 131 | 132 | .svg-controls.controls-mode-icon .zoom-slider-wrapper { 133 | width: var(--bt-svg-viewer-slider-width-icon, 200px); 134 | } 135 | 136 | .svg-controls.controls-vertical .zoom-slider-wrapper { 137 | width: 100%; 138 | } 139 | 140 | .svg-controls .zoom-slider { 141 | width: 100%; 142 | height: 6px; 143 | appearance: none; 144 | -webkit-appearance: none; 145 | background: var(--bt-svg-viewer-slider-track, rgba(0, 0, 0, 0.15)); 146 | border-radius: 999px; 147 | outline: none; 148 | } 149 | 150 | .svg-controls .zoom-slider::-webkit-slider-thumb { 151 | -webkit-appearance: none; 152 | appearance: none; 153 | width: 18px; 154 | height: 18px; 155 | border-radius: 50%; 156 | background: var(--bt-svg-viewer-button-fill, #0073aa); 157 | border: 2px solid var(--bt-svg-viewer-button-border, #0073aa); 158 | cursor: pointer; 159 | box-shadow: 0 0 0 2px #fff; 160 | } 161 | 162 | .svg-controls .zoom-slider::-moz-range-thumb { 163 | width: 18px; 164 | height: 18px; 165 | border-radius: 50%; 166 | background: var(--bt-svg-viewer-button-fill, #0073aa); 167 | border: 2px solid var(--bt-svg-viewer-button-border, #0073aa); 168 | cursor: pointer; 169 | } 170 | 171 | .svg-controls .zoom-slider:focus-visible { 172 | outline: 2px solid var(--bt-svg-viewer-button-fill, #0073aa); 173 | outline-offset: 2px; 174 | } 175 | 176 | .bt-svg-viewer-btn { 177 | display: inline-flex; 178 | align-items: center; 179 | gap: 6px; 180 | padding: 8px 16px; 181 | background-color: var(--bt-svg-viewer-button-fill, #0073aa); 182 | color: var(--bt-svg-viewer-button-text, #fff); 183 | border: 1px solid var(--bt-svg-viewer-button-border, #0073aa); 184 | border-radius: 4px; 185 | cursor: pointer; 186 | font-size: 14px; 187 | font-weight: 500; 188 | transition: background-color 0.2s ease, border-color 0.2s ease, 189 | transform 0.1s ease; 190 | white-space: nowrap; 191 | } 192 | .bt-svg-viewer-btn.is-disabled, 193 | .bt-svg-viewer-btn:disabled { 194 | background-color: #c9ccd1; 195 | border-color: #b2b5ba; 196 | color: #f1f1f1; 197 | cursor: not-allowed; 198 | opacity: 0.65; 199 | } 200 | .bt-svg-viewer-btn.is-disabled:hover, 201 | .bt-svg-viewer-btn:disabled:hover { 202 | background-color: #c9ccd1; 203 | } 204 | 205 | .bt-svg-viewer-btn .btn-icon { 206 | font-size: 16px; 207 | line-height: 1; 208 | } 209 | 210 | .bt-svg-viewer-btn .btn-text { 211 | font-size: 14px; 212 | line-height: 1.3; 213 | } 214 | 215 | .bt-svg-viewer-btn .btn-icon svg { 216 | display: block; 217 | width: 16px; 218 | height: 16px; 219 | } 220 | 221 | .bt-svg-viewer-btn:hover { 222 | background-color: var( 223 | --bt-svg-viewer-button-hover, 224 | var(--bt-svg-viewer-button-fill, #0073aa) 225 | ); 226 | } 227 | 228 | .bt-svg-viewer-btn:active { 229 | transform: scale(0.98); 230 | } 231 | 232 | .svg-controls .divider { 233 | width: 1px; 234 | height: 24px; 235 | background-color: #ddd; 236 | margin: 0 8px; 237 | } 238 | 239 | .svg-controls.controls-vertical .divider { 240 | width: 100%; 241 | height: 1px; 242 | margin: 8px 0; 243 | } 244 | 245 | .zoom-display { 246 | font-size: 14px; 247 | color: #666; 248 | min-width: 60px; 249 | text-align: center; 250 | padding: 4px 8px; 251 | } 252 | 253 | .controls-mode-icon .btn-text { 254 | display: none; 255 | } 256 | 257 | .controls-mode-icon .bt-svg-viewer-btn { 258 | padding: 8px; 259 | justify-content: center; 260 | } 261 | 262 | .controls-mode-icon .bt-svg-viewer-btn .btn-icon svg { 263 | width: 20px; 264 | height: 20px; 265 | } 266 | 267 | .controls-mode-text .btn-icon { 268 | display: none; 269 | } 270 | 271 | .controls-style-compact .bt-svg-viewer-btn { 272 | padding: 6px 12px; 273 | font-size: 13px; 274 | } 275 | 276 | .controls-style-compact .svg-controls { 277 | gap: 8px; 278 | padding: 12px; 279 | } 280 | 281 | .controls-style-labels-on-hover .btn-text { 282 | opacity: 0; 283 | max-width: 0; 284 | overflow: hidden; 285 | transition: opacity 0.2s ease, max-width 0.2s ease; 286 | } 287 | 288 | .controls-style-labels-on-hover .bt-svg-viewer-btn:hover .btn-text, 289 | .controls-style-labels-on-hover .bt-svg-viewer-btn:focus .btn-text, 290 | .controls-style-labels-on-hover .bt-svg-viewer-btn:focus-visible .btn-text { 291 | opacity: 1; 292 | max-width: 200px; 293 | } 294 | 295 | .svg-controls.controls-vertical .bt-svg-viewer-btn { 296 | width: 100%; 297 | justify-content: center; 298 | } 299 | 300 | .svg-controls.controls-align-aligncenter { 301 | justify-content: center; 302 | } 303 | 304 | .svg-controls.controls-align-alignright { 305 | justify-content: flex-end; 306 | } 307 | 308 | .svg-controls.controls-align-alignleft { 309 | justify-content: flex-start; 310 | } 311 | 312 | .svg-controls.controls-vertical.controls-align-alignleft { 313 | align-items: flex-start; 314 | } 315 | 316 | .svg-controls.controls-vertical.controls-align-alignright { 317 | align-items: flex-end; 318 | } 319 | 320 | .svg-controls.controls-vertical.controls-align-aligncenter { 321 | align-items: center; 322 | } 323 | 324 | .bt-svg-viewer-main.controls-align-aligncenter .svg-controls { 325 | justify-content: center; 326 | } 327 | 328 | .bt-svg-viewer-main.controls-align-alignright .svg-controls { 329 | justify-content: flex-end; 330 | } 331 | 332 | .bt-svg-viewer-main.controls-align-alignleft .svg-controls { 333 | justify-content: flex-start; 334 | } 335 | 336 | .svg-controls.controls-vertical .coord-output { 337 | margin-left: 0; 338 | margin-top: 4px; 339 | } 340 | 341 | .svg-controls.controls-vertical .zoom-display { 342 | align-self: center; 343 | } 344 | 345 | .svg-container { 346 | flex: 0 0 auto; 347 | width: 100%; 348 | overflow: auto; 349 | background-color: #fff; 350 | position: relative; 351 | display: block; 352 | scroll-behavior: smooth; 353 | } 354 | 355 | .bt-svg-viewer-wrapper.pan-mode-drag .svg-container { 356 | cursor: grab; 357 | touch-action: pan-x pan-y; 358 | } 359 | 360 | .bt-svg-viewer-wrapper.pan-mode-drag .svg-container.is-dragging { 361 | cursor: grabbing; 362 | } 363 | 364 | .bt-svg-viewer-wrapper.pan-mode-drag .svg-container.is-dragging, 365 | .bt-svg-viewer-wrapper.pan-mode-drag .svg-container.is-dragging * { 366 | user-select: none; 367 | } 368 | 369 | .bt-svg-viewer-caption { 370 | border-top: 1px solid #ddd; 371 | } 372 | 373 | .svg-viewport { 374 | display: inline-block; 375 | transform-origin: top left; 376 | transition: none; 377 | } 378 | 379 | .svg-container svg { 380 | display: block; 381 | background-color: #f3f3f3; 382 | } 383 | 384 | /* Responsive adjustments */ 385 | @media (max-width: 768px) { 386 | .bt-svg-viewer-btn { 387 | padding: 6px 12px; 388 | font-size: 13px; 389 | } 390 | 391 | .bt-svg-viewer-btn .btn-icon { 392 | font-size: 14px; 393 | } 394 | 395 | .svg-controls { 396 | gap: 8px; 397 | padding: 12px; 398 | } 399 | 400 | .zoom-display { 401 | font-size: 12px; 402 | } 403 | } 404 | 405 | /* Fix for WordPress admin/editor environments */ 406 | .wp-block-html .bt-svg-viewer-wrapper { 407 | max-width: 100%; 408 | } 409 | 410 | .bt-svg-viewer-wrapper .svg-controls .zoom-display { 411 | display: inline-flex; 412 | align-items: center; 413 | gap: 0.25rem; 414 | justify-content: center; 415 | } 416 | 417 | .bt-svg-viewer-wrapper .svg-controls .coord-output { 418 | margin-left: 0.75rem; 419 | font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", 420 | monospace; 421 | font-size: 0.85rem; 422 | color: #444; 423 | } 424 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # BT SVG Viewer 3 | 4 | Embed large SVG diagrams in WordPress with zoom, pan, center, and authoring tools. Recent releases add a visual preset editor, icon-based controls, deeper shortcode options, and configurable button colors. 5 | 6 | --- 7 | 8 | ## Contents 9 | 10 | - [Contents](#contents) 11 | - [Installation](#installation) 12 | - [Quick Start](#quick-start) 13 | - [Shortcode Reference](#shortcode-reference) 14 | - [`controls_buttons` Cheat Sheet](#controls_buttons-cheat-sheet) 15 | - [Admin Preset Editor](#admin-preset-editor) 16 | - [Location](#location) 17 | - [Fields](#fields) 18 | - [Preview Pane](#preview-pane) 19 | - [Preset Shortcodes](#preset-shortcodes) 20 | - [Defaults Tab](#defaults-tab) 21 | - [Using Presets in Posts/Pages](#using-presets-in-postspages) 22 | - [Preview Workflow](#preview-workflow) 23 | - [Examples](#examples) 24 | - [Styling (CSS Hooks)](#styling-css-hooks) 25 | - [Tips \& Troubleshooting](#tips--troubleshooting) 26 | - [SVG Preparation](#svg-preparation) 27 | - [Common Issues](#common-issues) 28 | - [Debugging](#debugging) 29 | - [Changelog Highlights (1.1.0)](#changelog-highlights-110) 30 | - [License \& Credits](#license--credits) 31 | 32 | --- 33 | 34 | ## Installation 35 | 36 | 1. **Unzip the plugin archive** 37 | - Download [bt-svg-viewer.zip](github.com/ttscoff/bt-svg-viewer/releases/latest/download/bt-svg-viewer.zip). 38 | - Unzip it locally; you will get a folder named `bt-svg-viewer`. 39 | 2. **Upload the plugin** 40 | - Copy the entire `bt-svg-viewer` folder into your WordPress installation at `/wp-content/plugins/`. 41 | 3. **Activate** 42 | - In the WordPress admin, navigate to **Plugins** and click **Activate** on ???BT SVG Viewer???. 43 | 44 | --- 45 | 46 | ## Quick Start 47 | 48 | ```text 49 | [btsvviewer src="/wp-content/uploads/diagrams/system-map.svg"] 50 | ``` 51 | 52 | - Place the shortcode in a classic editor, Gutenberg shortcode block, or template. 53 | - The SVG renders with default height (600px), zoom controls, pan/scroll behaviour, keyboard shortcuts, and responsive layout. Zoom buttons now gray out at the minimum/maximum zoom to make the limits obvious to visitors. 54 | - Existing shortcodes created before the rename continue to render, so archived posts don???t need updates. 55 | 56 | --- 57 | 58 | ## Shortcode Reference 59 | 60 | | Attribute | Type | Default | Description | 61 | | -------------------------------------------------- | ----------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | 62 | | `src` | string (required) | ??? | SVG URL. Supports absolute URLs, `/absolute/path.svg`, or relative to the uploads directory. | 63 | | `height` | string | `600px` | CSS height of the viewer. Accepts px, vh, %, etc. | 64 | | `class` | string | ??? | Additional class appended to the wrapper. | 65 | | `zoom` | number | `100` | Initial zoom percentage. | 66 | | `min_zoom` | number | `25` | Minimum zoom percentage allowed. | 67 | | `max_zoom` | number | `800` | Maximum zoom percentage allowed. | 68 | | `zoom_step` | number | `10` | Increment used by buttons/keyboard shortcuts. | 69 | | `initial_zoom` | number | ??? | Alias captured when presets save the preview state. Overrides `zoom` if present. | 70 | | `pan` / `pan_mode` | `scroll` or `drag` | `scroll` | Toggle between scroll-wheel panning and click-drag panning. Drag is enforced when zoom modes require it. | 71 | | `zoom_mode` / `zoom_behavior` / `zoom_interaction` | `super_scroll` / `scroll` / `click` | `super_scroll` | Choose how wheel and modifier gestures zoom: Cmd/Ctrl + wheel (`super_scroll`), every wheel (`scroll`), or Cmd/Ctrl-click & Alt-click (`click`). | 72 | | `center_x` / `center_y` | number | ??? | Manual center point in SVG units. Defaults to viewBox center. | 73 | | `show_coords` | boolean | `false` | Appends ???Copy Center??? button for debugging coordinate values. | 74 | | `controls_position` | `top`/`bottom`/`left`/`right` | `top` | Placement of the entire control group. | 75 | | `controls_buttons` | string | `both` | Comma-delimited mode/align/button list. See table below (supports `slider`). | 76 | | `title` | string | ??? | Optional heading above the viewer. HTML allowed. | 77 | | `caption` | string | ??? | Optional caption below the viewer. HTML allowed. | 78 | | `button_fill` / `button_background` / `button_bg` | color string | theme default (`#0073aa`) | Button background color. Aliases exist for backwards compatibility (all map to `button_fill`). | 79 | | `button_border` | color string | matches fill | Outline color for buttons. Blank inherits the fill color. | 80 | | `button_foreground` / `button_fg` | color string | `#ffffff` | Text and icon color for buttons. Blank uses the default. | 81 | | `id` | number | ??? | Reference a saved preset (admin). Inline attributes override preset values. | 82 | 83 | > Changing the interaction defaults automatically inserts a helper caption (e.g. ???Cmd/Ctrl-click to zoom in??????) above your custom caption so visitors know the gesture. 84 | 85 | ### `controls_buttons` Cheat Sheet 86 | 87 | ```text 88 | controls_buttons="MODE,ALIGNMENT,BUTTON_1,BUTTON_2,..." 89 | ``` 90 | 91 | Mode keywords (optional): 92 | 93 | - `both` (default) 94 | - `icon` 95 | - `text` 96 | - `compact` 97 | - `labels_on_hover` 98 | - `minimal` 99 | - `hidden` / `none` 100 | 101 | Alignment keywords (optional, anywhere in list): 102 | 103 | - `alignleft` 104 | - `aligncenter` 105 | - `alignright` 106 | 107 | Additional keywords: 108 | 109 | - `slider` ??? replaces zoom buttons with a live-updating range input. Combine with `icon`/`text`/`both` for layout. Use `custom:slider,zoom_in,zoom_out` to show both slider and buttons. 110 | 111 | Button names (pick any order): 112 | 113 | - `zoom_in`, `zoom_out`, `reset`, `center`, `coords` *(coords button only renders if `show_coords="true"`)* 114 | 115 | Example: 116 | 117 | ```text 118 | [btsvviewer 119 | src="/uploads/system-map.svg" 120 | controls_position="right" 121 | controls_buttons="slider,icon,aligncenter,reset,center" 122 | ] 123 | ``` 124 | 125 | --- 126 | 127 | ## Admin Preset Editor 128 | 129 | ### Location 130 | 131 | `WordPress Dashboard ??? BT SVG Viewer ??? Presets` 132 | 133 | ### Fields 134 | 135 | | Field | Description | 136 | | --------------------------- | ------------------------------------------------------------------------------ | 137 | | **SVG Source URL** | Choose or upload an SVG from the media library. Required before preview loads. | 138 | | **Viewer Height** | Same as shortcode `height`. | 139 | | **Zoom Settings** | Minimum, maximum, step, and initial zoom percentages. | 140 | | **Center Coordinates** | Override auto centering with explicit `center_x`/`center_y`. | 141 | | **Controls Position** | Dropdown for `top`, `bottom`, `left`, `right`. | 142 | | **Controls Buttons/Layout** | Text field following the `MODE,ALIGN,buttons???` pattern described above. | 143 | | **Button Colors** | Three color pickers for fill, border, and foreground (text/icon) colors. | 144 | | **Title & Caption** | Optional, displayed above/below the viewer wrapper. | 145 | 146 | ### Preview Pane 147 | 148 | - **Load / Refresh Preview**: Injects the SVG using the current field values. 149 | - **Use Current View for Initial State**: Captures the visible zoom level and center point from the preview and writes them back into the form (ideal for fine-tuning coordinates visually). 150 | - **Copy Center**: When `show_coords` is enabled, the button copies coordinates to the clipboard. 151 | - Zoom controls in the preview (and front end) now snap to their minimum/maximum, disable the corresponding buttons, and keep your pointer-focused zoom locked to the selected point???what you see in the preview is exactly what site visitors experience. 152 | 153 | ### Preset Shortcodes 154 | 155 | - At the top of the preset editor and in the presets list table you???ll find a copy-ready snippet in the form of `[btsvviewer id="123"]`. 156 | - Click **Copy** to put the shortcode on the clipboard without selecting manually. 157 | 158 | ### Defaults Tab 159 | 160 | - Visit **BT SVG Viewer ??? Presets ??? Defaults** to seed the fields used when creating a new preset. 161 | - The panel now includes **Enable asset cache busting for debugging**, which appends a time-based suffix to scripts and styles. It is automatically active on hosts that start with `dev.` or `wptest.` and can be toggled manually when you need to defeat browser caching. 162 | 163 | --- 164 | 165 | ## Using Presets in Posts/Pages 166 | 167 | 1. Create or edit a preset as described above. 168 | 2. Copy the generated shortcode (`[btsvviewer id="123"]`). 169 | 3. Paste into: 170 | - Classic editor (Visual tab: use Shortcode block or paste directly). 171 | - Block editor (Shortcode block or HTML block). 172 | - Template files via `do_shortcode()`. 173 | 4. Override on a per-use basis if needed: 174 | 175 | ```text 176 | [btsvviewer id="123" controls_buttons="icon,alignright,zoom_in,zoom_out"] 177 | ``` 178 | 179 | --- 180 | 181 | ## Preview Workflow 182 | 183 | 1. **Load / Refresh Preview** once the SVG path is entered. 184 | 2. Drag or scroll the SVG to the desired default view. 185 | 3. **Use Current View for Initial State** to stash those coordinates/zoom. 186 | 4. Optionally toggle `show_coords` to display the current x/y values of the viewport center. 187 | 5. Save/publish the preset and test the shortcode on the front end. 188 | 189 | --- 190 | 191 | ## Examples 192 | 193 | ```text 194 | [btsvviewer src="/uploads/floorplan.svg"] 195 | ``` 196 | 197 | ```text 198 | [btsvviewer 199 | src="/uploads/system-map.svg" 200 | height="100vh" 201 | controls_position="bottom" 202 | controls_buttons="compact,aligncenter,zoom_in,zoom_out,reset,center" 203 | zoom="175" 204 | min_zoom="50" 205 | max_zoom="400" 206 | ] 207 | ``` 208 | 209 | ```text 210 | [btsvviewer 211 | id="42" 212 | controls_buttons="custom:slider,zoom_in,zoom_out,reset" 213 | ] 214 | ``` 215 | 216 | ```text 217 | [btsvviewer 218 | src="/uploads/mind-map.svg" 219 | show_coords="true" 220 | controls_buttons="icon,alignleft,zoom_in,zoom_out,reset,center,coords" 221 | ] 222 | ``` 223 | 224 | ```text 225 | [btsvviewer 226 | src="/uploads/campus-map.svg" 227 | button_bg="#2f855a" 228 | button_border="#22543d" 229 | button_fg="#ffffff" 230 | controls_buttons="both,aligncenter,zoom_in,zoom_out,reset,center" 231 | ] 232 | ``` 233 | 234 | --- 235 | 236 | ## Styling (CSS Hooks) 237 | 238 | Wrapper classes added by the plugin: 239 | 240 | - `.bt-svg-viewer-wrapper` ??? outer container 241 | - `.bt-svg-viewer-main` ??? wraps controls and SVG container 242 | - `.svg-controls` ??? control bar 243 | - `.controls-position-{top|bottom|left|right}` 244 | - `.controls-mode-{icon|text|both|compact|labels-on-hover|minimal}` 245 | - `.controls-align-{alignleft|aligncenter|alignright}` 246 | - `.bt-svg-viewer-btn`, `.btn-icon`, `.btn-text` 247 | - `.svg-container`, `.svg-viewport`, `.coord-output`, `.zoom-display` 248 | - `.zoom-slider-wrapper`, `.zoom-slider` 249 | 250 | Button colors are powered by CSS custom properties on the wrapper. Shortcode attributes and preset color pickers set these values, but you can override them manually: 251 | 252 | ```css 253 | .bt-svg-viewer-wrapper { 254 | --bt-svg-viewer-button-fill: #1d4ed8; 255 | --bt-svg-viewer-button-border: #1d4ed8; 256 | --bt-svg-viewer-button-hover: #1e40af; 257 | --bt-svg-viewer-button-text: #ffffff; 258 | --bt-svg-viewer-slider-width: 240px; /* optional: tweak slider width */ 259 | } 260 | ``` 261 | 262 | Example overrides: 263 | 264 | ```css 265 | .bt-svg-viewer-wrapper.custom-map { 266 | border-radius: 12px; 267 | overflow: hidden; 268 | } 269 | 270 | .bt-svg-viewer-wrapper.custom-map .svg-controls { 271 | background: #111; 272 | color: #fff; 273 | gap: 6px; 274 | } 275 | 276 | .bt-svg-viewer-wrapper.custom-map .bt-svg-viewer-btn { 277 | background: rgba(255,255,255,0.1); 278 | border: 1px solid rgba(255,255,255,0.3); 279 | } 280 | 281 | .bt-svg-viewer-wrapper.custom-map .bt-svg-viewer-btn:hover { 282 | background: rgba(255,255,255,0.25); 283 | } 284 | ``` 285 | 286 | --- 287 | 288 | ## Tips & Troubleshooting 289 | 290 | ### SVG Preparation 291 | 292 | - Remove hard-coded width/height if possible; rely on `viewBox`. 293 | - Ensure the SVG renders correctly when opened directly in a browser. 294 | - Compress large SVGs to keep loading snappy. 295 | 296 | ### Common Issues 297 | 298 | - **Blank viewer**: check for 404 on the SVG path, confirm the file is accessible without authentication. 299 | - **Scaling looks wrong**: verify the SVG has an accurate `viewBox`. 300 | - **Controls disappear**: check `controls_buttons` for `hidden` or empty button list. 301 | - **Clipboard errors**: Some browsers require user interaction for `Copy Center`; the plugin falls back to a prompt. 302 | 303 | ### Debugging 304 | 305 | - Toggle `show_coords="true"` or inspect `window.btsvviewerInstances['viewer-id']` to troubleshoot zoom, center, or scroll behaviour. 306 | - Use the Defaults tab???s **Enable asset cache busting for debugging** switch if your browser clings to stale copies of the viewer script or styles. 307 | 308 | --- 309 | 310 | ## Changelog Highlights (1.1.0) 311 | 312 | - **Pan/Zoom Interaction Modes**: Shortcode and presets can now request `pan_mode="drag"` or `zoom_mode="scroll"` / `click"`, with the front end auto-explaining gesture hints to visitors. 313 | - **Smooth Cursor-Focused Zoom**: Wheel, slider, and modifier-click zoom animate between stops and keep the point under the pointer locked in place. 314 | - **Responsive Drag Panning**: Dragging now tracks 1:1 with the pointer and ignores stray wheel events so diagonal swipes stay fluid. 315 | - **Dev-Friendly Cache Busting**: The Defaults tab adds an ???Enable asset cache busting??? switch (also auto-enabled for `dev.*` and `wptest.*` hosts) to force fresh JS/CSS while testing. 316 | 317 | Full changelog lives in the repository???s `CHANGELOG.md`. 318 | 319 | --- 320 | 321 | ## License & Credits 322 | 323 | - License: GPL-2.0 (same as WordPress). 324 | - Built to make large interactive diagrams pleasant to navigate inside WordPress. 325 | -------------------------------------------------------------------------------- /src/_README.md: -------------------------------------------------------------------------------- 1 | 2 | # BT SVG Viewer 3 | 4 | Embed large SVG diagrams in WordPress with zoom, pan, center, and authoring tools. Recent releases add a visual preset editor, icon-based controls, deeper shortcode options, and configurable button colors. 5 | 6 | --- 7 | 8 | ## Contents 9 | 10 | - [Contents](#contents) 11 | - [Installation](#installation) 12 | - [Quick Start](#quick-start) 13 | - [Shortcode Reference](#shortcode-reference) 14 | - [`controls_buttons` Cheat Sheet](#controls_buttons-cheat-sheet) 15 | - [Admin Preset Editor](#admin-preset-editor) 16 | - [Location](#location) 17 | - [Fields](#fields) 18 | - [Preview Pane](#preview-pane) 19 | - [Preset Shortcodes](#preset-shortcodes) 20 | - [Defaults Tab](#defaults-tab) 21 | - [Using Presets in Posts/Pages](#using-presets-in-postspages) 22 | - [Preview Workflow](#preview-workflow) 23 | - [Examples](#examples) 24 | - [Styling (CSS Hooks)](#styling-css-hooks) 25 | - [Tips \& Troubleshooting](#tips--troubleshooting) 26 | - [SVG Preparation](#svg-preparation) 27 | - [Common Issues](#common-issues) 28 | - [Debugging](#debugging) 29 | - [Changelog Highlights (1.1.0)](#changelog-highlights-110) 30 | - [License \& Credits](#license--credits) 31 | 32 | --- 33 | 34 | ## Installation 35 | 36 | 1. **Unzip the plugin archive** 37 | - Download [bt-svg-viewer.zip](github.com/ttscoff/bt-svg-viewer/releases/latest/download/bt-svg-viewer.zip). 38 | - Unzip it locally; you will get a folder named `bt-svg-viewer`. 39 | 2. **Upload the plugin** 40 | - Copy the entire `bt-svg-viewer` folder into your WordPress installation at `/wp-content/plugins/`. 41 | 3. **Activate** 42 | - In the WordPress admin, navigate to **Plugins** and click **Activate** on “BT SVG Viewer”. 43 | 44 | --- 45 | 46 | ## Quick Start 47 | 48 | ```text 49 | [btsvviewer src="/wp-content/uploads/diagrams/system-map.svg"] 50 | ``` 51 | 52 | - Place the shortcode in a classic editor, Gutenberg shortcode block, or template. 53 | - The SVG renders with default height (600px), zoom controls, pan/scroll behaviour, keyboard shortcuts, and responsive layout. Zoom buttons now gray out at the minimum/maximum zoom to make the limits obvious to visitors. 54 | - Existing shortcodes created before the rename continue to render, so archived posts don’t need updates. 55 | 56 | --- 57 | 58 | ## Shortcode Reference 59 | 60 | | Attribute | Type | Default | Description | 61 | | -------------------------------------------------- | ----------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | 62 | | `src` | string (required) | – | SVG URL. Supports absolute URLs, `/absolute/path.svg`, or relative to the uploads directory. | 63 | | `height` | string | `600px` | CSS height of the viewer. Accepts px, vh, %, etc. | 64 | | `class` | string | – | Additional class appended to the wrapper. | 65 | | `zoom` | number | `100` | Initial zoom percentage. | 66 | | `min_zoom` | number | `25` | Minimum zoom percentage allowed. | 67 | | `max_zoom` | number | `800` | Maximum zoom percentage allowed. | 68 | | `zoom_step` | number | `10` | Increment used by buttons/keyboard shortcuts. | 69 | | `initial_zoom` | number | – | Alias captured when presets save the preview state. Overrides `zoom` if present. | 70 | | `pan` / `pan_mode` | `scroll` or `drag` | `scroll` | Toggle between scroll-wheel panning and click-drag panning. Drag is enforced when zoom modes require it. | 71 | | `zoom_mode` / `zoom_behavior` / `zoom_interaction` | `super_scroll` / `scroll` / `click` | `super_scroll` | Choose how wheel and modifier gestures zoom: Cmd/Ctrl + wheel (`super_scroll`), every wheel (`scroll`), or Cmd/Ctrl-click & Alt-click (`click`). | 72 | | `center_x` / `center_y` | number | – | Manual center point in SVG units. Defaults to viewBox center. | 73 | | `show_coords` | boolean | `false` | Appends “Copy Center” button for debugging coordinate values. | 74 | | `controls_position` | `top`/`bottom`/`left`/`right` | `top` | Placement of the entire control group. | 75 | | `controls_buttons` | string | `both` | Comma-delimited mode/align/button list. See table below (supports `slider`). | 76 | | `title` | string | – | Optional heading above the viewer. HTML allowed. | 77 | | `caption` | string | – | Optional caption below the viewer. HTML allowed. | 78 | | `button_fill` / `button_background` / `button_bg` | color string | theme default (`#0073aa`) | Button background color. Aliases exist for backwards compatibility (all map to `button_fill`). | 79 | | `button_border` | color string | matches fill | Outline color for buttons. Blank inherits the fill color. | 80 | | `button_foreground` / `button_fg` | color string | `#ffffff` | Text and icon color for buttons. Blank uses the default. | 81 | | `id` | number | – | Reference a saved preset (admin). Inline attributes override preset values. | 82 | 83 | > Changing the interaction defaults automatically inserts a helper caption (e.g. “Cmd/Ctrl-click to zoom in…”) above your custom caption so visitors know the gesture. 84 | 85 | ### `controls_buttons` Cheat Sheet 86 | 87 | ```text 88 | controls_buttons="MODE,ALIGNMENT,BUTTON_1,BUTTON_2,..." 89 | ``` 90 | 91 | Mode keywords (optional): 92 | 93 | - `both` (default) 94 | - `icon` 95 | - `text` 96 | - `compact` 97 | - `labels_on_hover` 98 | - `minimal` 99 | - `hidden` / `none` 100 | 101 | Alignment keywords (optional, anywhere in list): 102 | 103 | - `alignleft` 104 | - `aligncenter` 105 | - `alignright` 106 | 107 | Additional keywords: 108 | 109 | - `slider` – replaces zoom buttons with a live-updating range input. Combine with `icon`/`text`/`both` for layout. Use `custom:slider,zoom_in,zoom_out` to show both slider and buttons. 110 | 111 | Button names (pick any order): 112 | 113 | - `zoom_in`, `zoom_out`, `reset`, `center`, `coords` *(coords button only renders if `show_coords="true"`)* 114 | 115 | Example: 116 | 117 | ```text 118 | [btsvviewer 119 | src="/uploads/system-map.svg" 120 | controls_position="right" 121 | controls_buttons="slider,icon,aligncenter,reset,center" 122 | ] 123 | ``` 124 | 125 | --- 126 | 127 | ## Admin Preset Editor 128 | 129 | ### Location 130 | 131 | `WordPress Dashboard → BT SVG Viewer → Presets` 132 | 133 | ### Fields 134 | 135 | | Field | Description | 136 | | --------------------------- | ------------------------------------------------------------------------------ | 137 | | **SVG Source URL** | Choose or upload an SVG from the media library. Required before preview loads. | 138 | | **Viewer Height** | Same as shortcode `height`. | 139 | | **Zoom Settings** | Minimum, maximum, step, and initial zoom percentages. | 140 | | **Center Coordinates** | Override auto centering with explicit `center_x`/`center_y`. | 141 | | **Controls Position** | Dropdown for `top`, `bottom`, `left`, `right`. | 142 | | **Controls Buttons/Layout** | Text field following the `MODE,ALIGN,buttons…` pattern described above. | 143 | | **Button Colors** | Three color pickers for fill, border, and foreground (text/icon) colors. | 144 | | **Title & Caption** | Optional, displayed above/below the viewer wrapper. | 145 | 146 | ### Preview Pane 147 | 148 | - **Load / Refresh Preview**: Injects the SVG using the current field values. 149 | - **Use Current View for Initial State**: Captures the visible zoom level and center point from the preview and writes them back into the form (ideal for fine-tuning coordinates visually). 150 | - **Copy Center**: When `show_coords` is enabled, the button copies coordinates to the clipboard. 151 | - Zoom controls in the preview (and front end) now snap to their minimum/maximum, disable the corresponding buttons, and keep your pointer-focused zoom locked to the selected point—what you see in the preview is exactly what site visitors experience. 152 | 153 | ### Preset Shortcodes 154 | 155 | - At the top of the preset editor and in the presets list table you’ll find a copy-ready snippet in the form of `[btsvviewer id="123"]`. 156 | - Click **Copy** to put the shortcode on the clipboard without selecting manually. 157 | 158 | ### Defaults Tab 159 | 160 | - Visit **BT SVG Viewer → Presets → Defaults** to seed the fields used when creating a new preset. 161 | - The panel now includes **Enable asset cache busting for debugging**, which appends a time-based suffix to scripts and styles. It is automatically active on hosts that start with `dev.` or `wptest.` and can be toggled manually when you need to defeat browser caching. 162 | 163 | --- 164 | 165 | ## Using Presets in Posts/Pages 166 | 167 | 1. Create or edit a preset as described above. 168 | 2. Copy the generated shortcode (`[btsvviewer id="123"]`). 169 | 3. Paste into: 170 | - Classic editor (Visual tab: use Shortcode block or paste directly). 171 | - Block editor (Shortcode block or HTML block). 172 | - Template files via `do_shortcode()`. 173 | 4. Override on a per-use basis if needed: 174 | 175 | ```text 176 | [btsvviewer id="123" controls_buttons="icon,alignright,zoom_in,zoom_out"] 177 | ``` 178 | 179 | --- 180 | 181 | ## Preview Workflow 182 | 183 | 1. **Load / Refresh Preview** once the SVG path is entered. 184 | 2. Drag or scroll the SVG to the desired default view. 185 | 3. **Use Current View for Initial State** to stash those coordinates/zoom. 186 | 4. Optionally toggle `show_coords` to display the current x/y values of the viewport center. 187 | 5. Save/publish the preset and test the shortcode on the front end. 188 | 189 | --- 190 | 191 | ## Examples 192 | 193 | ```text 194 | [btsvviewer src="/uploads/floorplan.svg"] 195 | ``` 196 | 197 | ```text 198 | [btsvviewer 199 | src="/uploads/system-map.svg" 200 | height="100vh" 201 | controls_position="bottom" 202 | controls_buttons="compact,aligncenter,zoom_in,zoom_out,reset,center" 203 | zoom="175" 204 | min_zoom="50" 205 | max_zoom="400" 206 | ] 207 | ``` 208 | 209 | ```text 210 | [btsvviewer 211 | id="42" 212 | controls_buttons="custom:slider,zoom_in,zoom_out,reset" 213 | ] 214 | ``` 215 | 216 | ```text 217 | [btsvviewer 218 | src="/uploads/mind-map.svg" 219 | show_coords="true" 220 | controls_buttons="icon,alignleft,zoom_in,zoom_out,reset,center,coords" 221 | ] 222 | ``` 223 | 224 | ```text 225 | [btsvviewer 226 | src="/uploads/campus-map.svg" 227 | button_bg="#2f855a" 228 | button_border="#22543d" 229 | button_fg="#ffffff" 230 | controls_buttons="both,aligncenter,zoom_in,zoom_out,reset,center" 231 | ] 232 | ``` 233 | 234 | --- 235 | 236 | ## Styling (CSS Hooks) 237 | 238 | Wrapper classes added by the plugin: 239 | 240 | - `.bt-svg-viewer-wrapper` – outer container 241 | - `.bt-svg-viewer-main` – wraps controls and SVG container 242 | - `.svg-controls` – control bar 243 | - `.controls-position-{top|bottom|left|right}` 244 | - `.controls-mode-{icon|text|both|compact|labels-on-hover|minimal}` 245 | - `.controls-align-{alignleft|aligncenter|alignright}` 246 | - `.bt-svg-viewer-btn`, `.btn-icon`, `.btn-text` 247 | - `.svg-container`, `.svg-viewport`, `.coord-output`, `.zoom-display` 248 | - `.zoom-slider-wrapper`, `.zoom-slider` 249 | 250 | Button colors are powered by CSS custom properties on the wrapper. Shortcode attributes and preset color pickers set these values, but you can override them manually: 251 | 252 | ```css 253 | .bt-svg-viewer-wrapper { 254 | --bt-svg-viewer-button-fill: #1d4ed8; 255 | --bt-svg-viewer-button-border: #1d4ed8; 256 | --bt-svg-viewer-button-hover: #1e40af; 257 | --bt-svg-viewer-button-text: #ffffff; 258 | --bt-svg-viewer-slider-width: 240px; /* optional: tweak slider width */ 259 | } 260 | ``` 261 | 262 | Example overrides: 263 | 264 | ```css 265 | .bt-svg-viewer-wrapper.custom-map { 266 | border-radius: 12px; 267 | overflow: hidden; 268 | } 269 | 270 | .bt-svg-viewer-wrapper.custom-map .svg-controls { 271 | background: #111; 272 | color: #fff; 273 | gap: 6px; 274 | } 275 | 276 | .bt-svg-viewer-wrapper.custom-map .bt-svg-viewer-btn { 277 | background: rgba(255,255,255,0.1); 278 | border: 1px solid rgba(255,255,255,0.3); 279 | } 280 | 281 | .bt-svg-viewer-wrapper.custom-map .bt-svg-viewer-btn:hover { 282 | background: rgba(255,255,255,0.25); 283 | } 284 | ``` 285 | 286 | --- 287 | 288 | ## Tips & Troubleshooting 289 | 290 | ### SVG Preparation 291 | 292 | - Remove hard-coded width/height if possible; rely on `viewBox`. 293 | - Ensure the SVG renders correctly when opened directly in a browser. 294 | - Compress large SVGs to keep loading snappy. 295 | 296 | ### Common Issues 297 | 298 | - **Blank viewer**: check for 404 on the SVG path, confirm the file is accessible without authentication. 299 | - **Scaling looks wrong**: verify the SVG has an accurate `viewBox`. 300 | - **Controls disappear**: check `controls_buttons` for `hidden` or empty button list. 301 | - **Clipboard errors**: Some browsers require user interaction for `Copy Center`; the plugin falls back to a prompt. 302 | 303 | ### Debugging 304 | 305 | - Toggle `show_coords="true"` or inspect `window.btsvviewerInstances['viewer-id']` to troubleshoot zoom, center, or scroll behaviour. 306 | - Use the Defaults tab’s **Enable asset cache busting for debugging** switch if your browser clings to stale copies of the viewer script or styles. 307 | 308 | --- 309 | 310 | ## Changelog Highlights (1.1.0) 311 | 312 | - **Pan/Zoom Interaction Modes**: Shortcode and presets can now request `pan_mode="drag"` or `zoom_mode="scroll"` / `click"`, with the front end auto-explaining gesture hints to visitors. 313 | - **Smooth Cursor-Focused Zoom**: Wheel, slider, and modifier-click zoom animate between stops and keep the point under the pointer locked in place. 314 | - **Responsive Drag Panning**: Dragging now tracks 1:1 with the pointer and ignores stray wheel events so diagonal swipes stay fluid. 315 | - **Dev-Friendly Cache Busting**: The Defaults tab adds an “Enable asset cache busting” switch (also auto-enabled for `dev.*` and `wptest.*` hosts) to force fresh JS/CSS while testing. 316 | 317 | Full changelog lives in the repository’s `CHANGELOG.md`. 318 | 319 | --- 320 | 321 | ## License & Credits 322 | 323 | - License: GPL-2.0 (same as WordPress). 324 | - Built to make large interactive diagrams pleasant to navigate inside WordPress. 325 | 326 | -------------------------------------------------------------------------------- /bt-svg-viewer/admin/help.html: -------------------------------------------------------------------------------- 1 |

BT SVG Viewer

2 | 3 |

Embed large SVG diagrams in WordPress with zoom, pan, center, and authoring tools. Recent releases add a visual preset editor, icon-based controls, deeper shortcode options, and configurable button colors.

4 | 5 |
6 | 7 |

Contents

8 | 9 | 32 | 33 |
34 | 35 |

Installation

36 | 37 |
    38 |
  1. Unzip the plugin archive
  2. 39 |
40 | 41 |
    42 |
  • Download bt-svg-viewer.zip.
  • 43 |
  • Unzip it locally; you will get a folder named bt-svg-viewer.
  • 44 |
45 | 46 |
    47 |
  1. Upload the plugin
  2. 48 |
49 | 50 |
    51 |
  • Copy the entire bt-svg-viewer folder into your WordPress installation at /wp-content/plugins/.
  • 52 |
53 | 54 |
    55 |
  1. Activate
  2. 56 |
57 | 58 |
    59 |
  • In the WordPress admin, navigate to Plugins and click Activate on ???BT SVG Viewer???.
  • 60 |
61 | 62 |
63 | 64 |

Quick Start

65 | 66 |
[btsvviewer src="/wp-content/uploads/diagrams/system-map.svg"]
 67 | 
68 | 69 |
    70 |
  • Place the shortcode in a classic editor, Gutenberg shortcode block, or template.
  • 71 |
  • The SVG renders with default height (600px), zoom controls, pan/scroll behaviour, keyboard shortcuts, and responsive layout. Zoom buttons now gray out at the minimum/maximum zoom to make the limits obvious to visitors.
  • 72 |
  • Existing shortcodes created before the rename continue to render, so archived posts don???t need updates.
  • 73 |
74 | 75 |
76 | 77 |

Shortcode Reference

78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 |
Attribute Type Default Description
src string (required) ??? SVG URL. Supports absolute URLs, /absolute/path.svg, or relative to the uploads directory.
height string 600px CSS height of the viewer. Accepts px, vh, %, etc.
class string ??? Additional class appended to the wrapper.
zoom number 100 Initial zoom percentage.
min_zoom number 25 Minimum zoom percentage allowed.
max_zoom number 800 Maximum zoom percentage allowed.
zoom_step number 10 Increment used by buttons/keyboard shortcuts.
initial_zoom number ??? Alias captured when presets save the preview state. Overrides zoom if present.
pan / pan_mode scroll or drag scroll Toggle between scroll-wheel panning and click-drag panning. Drag is enforced when zoom modes require it.
zoom_mode / zoom_behavior / zoom_interaction super_scroll / scroll / click super_scroll Choose how wheel and modifier gestures zoom: Cmd/Ctrl + wheel (super_scroll), every wheel (scroll), or Cmd/Ctrl-click & Alt-click (click).
center_x / center_y number ??? Manual center point in SVG units. Defaults to viewBox center.
show_coords boolean false Appends ???Copy Center??? button for debugging coordinate values.
controls_position top/bottom/left/right top Placement of the entire control group.
controls_buttons string both Comma-delimited mode/align/button list. See table below (supports slider).
title string ??? Optional heading above the viewer. HTML allowed.
caption string ??? Optional caption below the viewer. HTML allowed.
button_fill / button_background / button_bg color string theme default (#0073aa) Button background color. Aliases exist for backwards compatibility (all map to button_fill).
button_border color string matches fill Outline color for buttons. Blank inherits the fill color.
button_foreground / button_fg color string #ffffff Text and icon color for buttons. Blank uses the default.
id number ??? Reference a saved preset (admin). Inline attributes override preset values.
219 | 220 |
221 |

Changing the interaction defaults automatically inserts a helper caption (e.g. ???Cmd/Ctrl-click to zoom in??????) above your custom caption so visitors know the gesture.

222 |
223 | 224 |

controls_buttons Cheat Sheet

225 | 226 |
controls_buttons="MODE,ALIGNMENT,BUTTON_1,BUTTON_2,..."
227 | 
228 | 229 |

Mode keywords (optional):

230 | 231 |
    232 |
  • both (default)
  • 233 |
  • icon
  • 234 |
  • text
  • 235 |
  • compact
  • 236 |
  • labels_on_hover
  • 237 |
  • minimal
  • 238 |
  • hidden / none
  • 239 |
240 | 241 |

Alignment keywords (optional, anywhere in list):

242 | 243 |
    244 |
  • alignleft
  • 245 |
  • aligncenter
  • 246 |
  • alignright
  • 247 |
248 | 249 |

Additional keywords:

250 | 251 |
    252 |
  • slider ??? replaces zoom buttons with a live-updating range input. Combine with icon/text/both for layout. Use custom:slider,zoom_in,zoom_out to show both slider and buttons.
  • 253 |
254 | 255 |

Button names (pick any order):

256 | 257 |
    258 |
  • zoom_in, zoom_out, reset, center, coords (coords button only renders if show_coords="true")
  • 259 |
260 | 261 |

Example:

262 | 263 |
[btsvviewer
264 |   src="/uploads/system-map.svg"
265 |   controls_position="right"
266 |   controls_buttons="slider,icon,aligncenter,reset,center"
267 | ]
268 | 
269 | 270 |
271 | 272 |

Admin Preset Editor

273 | 274 |

Location

275 | 276 |

WordPress Dashboard ??? BT SVG Viewer ??? Presets

277 | 278 |

Fields

279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 |
Field Description
SVG Source URL Choose or upload an SVG from the media library. Required before preview loads.
Viewer Height Same as shortcode height.
Zoom Settings Minimum, maximum, step, and initial zoom percentages.
Center Coordinates Override auto centering with explicit center_x/center_y.
Controls Position Dropdown for top, bottom, left, right.
Controls Buttons/Layout Text field following the MODE,ALIGN,buttons??? pattern described above.
Button Colors Three color pickers for fill, border, and foreground (text/icon) colors.
Title & Caption Optional, displayed above/below the viewer wrapper.
328 | 329 |

Preview Pane

330 | 331 |
    332 |
  • Load / Refresh Preview: Injects the SVG using the current field values.
  • 333 |
  • Use Current View for Initial State: Captures the visible zoom level and center point from the preview and writes them back into the form (ideal for fine-tuning coordinates visually).
  • 334 |
  • Copy Center: When show_coords is enabled, the button copies coordinates to the clipboard.
  • 335 |
  • Zoom controls in the preview (and front end) now snap to their minimum/maximum, disable the corresponding buttons, and keep your pointer-focused zoom locked to the selected point???what you see in the preview is exactly what site visitors experience.
  • 336 |
337 | 338 |

Preset Shortcodes

339 | 340 |
    341 |
  • At the top of the preset editor and in the presets list table you???ll find a copy-ready snippet in the form of [btsvviewer id="123"].
  • 342 |
  • Click Copy to put the shortcode on the clipboard without selecting manually.
  • 343 |
344 | 345 |

Defaults Tab

346 | 347 |
    348 |
  • Visit BT SVG Viewer ??? Presets ??? Defaults to seed the fields used when creating a new preset.
  • 349 |
  • The panel now includes Enable asset cache busting for debugging, which appends a time-based suffix to scripts and styles. It is automatically active on hosts that start with dev. or wptest. and can be toggled manually when you need to defeat browser caching.
  • 350 |
351 | 352 |
353 | 354 |

Using Presets in Posts/Pages

355 | 356 |
    357 |
  1. Create or edit a preset as described above.
  2. 358 |
  3. Copy the generated shortcode ([btsvviewer id="123"]).
  4. 359 |
  5. Paste into:
  6. 360 |
361 | 362 |
    363 |
  • Classic editor (Visual tab: use Shortcode block or paste directly).
  • 364 |
  • Block editor (Shortcode block or HTML block).
  • 365 |
  • Template files via do_shortcode().
  • 366 |
367 | 368 |
    369 |
  1. Override on a per-use basis if needed:
  2. 370 |
371 | 372 |
   [btsvviewer id="123" controls_buttons="icon,alignright,zoom_in,zoom_out"]
373 | 
374 | 
375 | 376 |
377 | 378 |

Preview Workflow

379 | 380 |
    381 |
  1. Load / Refresh Preview once the SVG path is entered.
  2. 382 |
  3. Drag or scroll the SVG to the desired default view.
  4. 383 |
  5. Use Current View for Initial State to stash those coordinates/zoom.
  6. 384 |
  7. Optionally toggle show_coords to display the current x/y values of the viewport center.
  8. 385 |
  9. Save/publish the preset and test the shortcode on the front end.
  10. 386 |
387 | 388 |
389 | 390 |

Examples

391 | 392 |
[btsvviewer src="/uploads/floorplan.svg"]
393 | 
394 | 395 |
[btsvviewer
396 |   src="/uploads/system-map.svg"
397 |   height="100vh"
398 |   controls_position="bottom"
399 |   controls_buttons="compact,aligncenter,zoom_in,zoom_out,reset,center"
400 |   zoom="175"
401 |   min_zoom="50"
402 |   max_zoom="400"
403 | ]
404 | 
405 | 406 |
[btsvviewer
407 |   id="42"
408 |   controls_buttons="custom:slider,zoom_in,zoom_out,reset"
409 | ]
410 | 
411 | 412 |
[btsvviewer
413 |   src="/uploads/mind-map.svg"
414 |   show_coords="true"
415 |   controls_buttons="icon,alignleft,zoom_in,zoom_out,reset,center,coords"
416 | ]
417 | 
418 | 419 |
[btsvviewer
420 |   src="/uploads/campus-map.svg"
421 |   button_bg="#2f855a"
422 |   button_border="#22543d"
423 |   button_fg="#ffffff"
424 |   controls_buttons="both,aligncenter,zoom_in,zoom_out,reset,center"
425 | ]
426 | 
427 | 428 |
429 | 430 |

Styling (CSS Hooks)

431 | 432 |

Wrapper classes added by the plugin:

433 | 434 |
    435 |
  • .bt-svg-viewer-wrapper ??? outer container
  • 436 |
  • .bt-svg-viewer-main ??? wraps controls and SVG container
  • 437 |
  • .svg-controls ??? control bar
  • 438 |
  • .controls-position-{top|bottom|left|right}
  • 439 |
  • .controls-mode-{icon|text|both|compact|labels-on-hover|minimal}
  • 440 |
  • .controls-align-{alignleft|aligncenter|alignright}
  • 441 |
  • .bt-svg-viewer-btn, .btn-icon, .btn-text
  • 442 |
  • .svg-container, .svg-viewport, .coord-output, .zoom-display
  • 443 |
  • .zoom-slider-wrapper, .zoom-slider
  • 444 |
445 | 446 |

Button colors are powered by CSS custom properties on the wrapper. Shortcode attributes and preset color pickers set these values, but you can override them manually:

447 | 448 |
.bt-svg-viewer-wrapper {
449 |   --bt-svg-viewer-button-fill: #1d4ed8;
450 |   --bt-svg-viewer-button-border: #1d4ed8;
451 |   --bt-svg-viewer-button-hover: #1e40af;
452 |   --bt-svg-viewer-button-text: #ffffff;
453 |   --bt-svg-viewer-slider-width: 240px;       /* optional: tweak slider width */
454 | }
455 | 
456 | 457 |

Example overrides:

458 | 459 |
.bt-svg-viewer-wrapper.custom-map {
460 |   border-radius: 12px;
461 |   overflow: hidden;
462 | }
463 | 
464 | .bt-svg-viewer-wrapper.custom-map .svg-controls {
465 |   background: #111;
466 |   color: #fff;
467 |   gap: 6px;
468 | }
469 | 
470 | .bt-svg-viewer-wrapper.custom-map .bt-svg-viewer-btn {
471 |   background: rgba(255,255,255,0.1);
472 |   border: 1px solid rgba(255,255,255,0.3);
473 | }
474 | 
475 | .bt-svg-viewer-wrapper.custom-map .bt-svg-viewer-btn:hover {
476 |   background: rgba(255,255,255,0.25);
477 | }
478 | 
479 | 480 |
481 | 482 |

Tips & Troubleshooting

483 | 484 |

SVG Preparation

485 | 486 |
    487 |
  • Remove hard-coded width/height if possible; rely on viewBox.
  • 488 |
  • Ensure the SVG renders correctly when opened directly in a browser.
  • 489 |
  • Compress large SVGs to keep loading snappy.
  • 490 |
491 | 492 |

Common Issues

493 | 494 |
    495 |
  • Blank viewer: check for 404 on the SVG path, confirm the file is accessible without authentication.
  • 496 |
  • Scaling looks wrong: verify the SVG has an accurate viewBox.
  • 497 |
  • Controls disappear: check controls_buttons for hidden or empty button list.
  • 498 |
  • Clipboard errors: Some browsers require user interaction for Copy Center; the plugin falls back to a prompt.
  • 499 |
500 | 501 |

Debugging

502 | 503 |
    504 |
  • Toggle show_coords="true" or inspect window.btsvviewerInstances['viewer-id'] to troubleshoot zoom, center, or scroll behaviour.
  • 505 |
  • Use the Defaults tab???s Enable asset cache busting for debugging switch if your browser clings to stale copies of the viewer script or styles.
  • 506 |
507 | 508 |
509 | 510 |

Changelog Highlights (1.1.0)

511 | 512 |
    513 |
  • Pan/Zoom Interaction Modes: Shortcode and presets can now request pan_mode="drag" or zoom_mode="scroll" / click", with the front end auto-explaining gesture hints to visitors.
  • 514 |
  • Smooth Cursor-Focused Zoom: Wheel, slider, and modifier-click zoom animate between stops and keep the point under the pointer locked in place.
  • 515 |
  • Responsive Drag Panning: Dragging now tracks 1:1 with the pointer and ignores stray wheel events so diagonal swipes stay fluid.
  • 516 |
  • Dev-Friendly Cache Busting: The Defaults tab adds an ???Enable asset cache busting??? switch (also auto-enabled for dev.* and wptest.* hosts) to force fresh JS/CSS while testing.
  • 517 |
518 | 519 |

Full changelog lives in the repository???s CHANGELOG.md.

520 | 521 |
522 | 523 |

License & Credits

524 | 525 |
    526 |
  • License: GPL-2.0 (same as WordPress).
  • 527 |
  • Built to make large interactive diagrams pleasant to navigate inside WordPress.
  • 528 |
529 | -------------------------------------------------------------------------------- /bt-svg-viewer/admin/js/admin.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | function getI18n(key, fallback) { 3 | if ( 4 | window.btsvviewerAdmin && 5 | btsvviewerAdmin.i18n && 6 | btsvviewerAdmin.i18n[key] 7 | ) { 8 | return btsvviewerAdmin.i18n[key]; 9 | } 10 | return fallback; 11 | } 12 | 13 | function updateStatus($status, message) { 14 | if (!$status || !$status.length) { 15 | return; 16 | } 17 | $status.text(message); 18 | const existingTimeout = $status.data("svgTimeoutId"); 19 | if (existingTimeout) { 20 | clearTimeout(existingTimeout); 21 | } 22 | const timeoutId = setTimeout(function () { 23 | $status.text(""); 24 | $status.removeData("svgTimeoutId"); 25 | }, 4000); 26 | $status.data("svgTimeoutId", timeoutId); 27 | } 28 | 29 | async function copyShortcode(shortcode, $status, successKey = "copySuccess") { 30 | if (!shortcode) { 31 | return; 32 | } 33 | 34 | const fallbackSuccess = 35 | successKey === "fullCopySuccess" 36 | ? "Full shortcode copied to clipboard." 37 | : "Shortcode copied to clipboard."; 38 | 39 | try { 40 | if (navigator.clipboard && navigator.clipboard.writeText) { 41 | await navigator.clipboard.writeText(shortcode); 42 | } else { 43 | const $temp = $("