Skip to content

[docs] Fixed various spelling mistakes and typos#1399

Closed
ApurveKaranwal wants to merge 4 commits into
openwisp:masterfrom
ApurveKaranwal:fix-typos
Closed

[docs] Fixed various spelling mistakes and typos#1399
ApurveKaranwal wants to merge 4 commits into
openwisp:masterfrom
ApurveKaranwal:fix-typos

Conversation

@ApurveKaranwal

Copy link
Copy Markdown

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #1398

Description of Changes

This PR fixes various spelling mistakes, grammatical errors, and typos across the codebase (including Python comments, docstrings, tests, and HTML templates).

The misspellings were identified using codespell as well as manual review. Some of the corrections include:

  • commiting -> committing
  • taks -> tasks
  • atleast -> at least
  • deafult -> default
  • Grammatical fixes in test comments (e.g., Selenium tests do not support).

These changes have zero impact on the system's core logic but improve code readability and maintainability.

Screenshot

N/A

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request fixes spelling and grammar mistakes in comments, docstrings, HTML template comments, subTest labels, and inline code comments across the config, connection, geo, and subnet_division modules. One user-facing validation error message string in subnet_division/base/models.py ("accommodate") and one schema error message in connection/commands.py ("at least") are also corrected. A test JSON payload in test_admin.py fixes a misspelled field key (descripiondescription). Additionally, selenium test utilities add defensive null-checks to safely iterate over browser logs when get_browser_logs() returns None, unsaved-changes detection is enhanced to persist the beforeunload dialog text via sessionStorage for reliable cross-page-refresh verification, and a new real-time address field update test with geocoding mocks is introduced. The PR also adds a default_validity_start() helper function and a validity_start DateTimeField to PKI models (AbstractCa and AbstractCert) to avoid Django warnings about naive datetimes under timezone support, with corresponding database migrations.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • nemesifier

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive Most changes are in-scope spelling/grammar fixes, but two non-trivial additions (default_validity_start function and validity_start DateTimeField to CA/Cert models) extend beyond the stated PR objective of fixing spelling mistakes. Clarify whether the validity_start DateTimeField additions address a separate requirement or if they should be split into a separate PR focused on documentation/typing improvements.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title '[docs] Fixed various spelling mistakes and typos' clearly describes the main changes and aligns with the linked issue, using the correct [docs] prefix as required.
Description check ✅ Passed The PR description includes all required sections: completed checklist, reference to issue #1398, clear description of changes with specific examples, and acknowledges the lack of applicable screenshots.
Linked Issues check ✅ Passed All code changes across 22 files directly address the requirements in issue #1398: fixing spelling mistakes (commiting→committing, taks→tasks, atleast→at least, deafult→default, acommodate→accommodate) and grammatical errors in comments, docstrings, and templates without affecting functionality.
Bug Fixes ✅ Passed PR is primarily a spelling/grammar fix addressing issue #1398 with zero impact on core functionality. Secondary infrastructure changes (DateTimeField defaults, test improvements) fix warnings/relia...
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openwisp_controller/connection/tests/test_notifications.py (1)

51-57: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align the comment with the actual notification types.

The block still mentions config_error / device_registered, but the code unregisters connection_is_not_working / connection_is_working, so the comment is misleading.

As per coding guidelines, comments should explain why code is shaped a certain way, and this one now documents the wrong thing.

🛠️ Suggested comment fix
-        # Unregister "config_error" and "device_registered" notification
-        # types to avoid getting raising ImproperlyConfigured exceptions
+        # Unregister the connection notification types so registration
+        # does not raise ImproperlyConfigured.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_controller/connection/tests/test_notifications.py` around lines 51 -
57, The comment in the block starting with "Simulates if 'default notification
type is already unregistered" incorrectly references the notification types
config_error and device_registered, but the actual code unregisters
connection_is_not_working and connection_is_working. Update the comment text to
accurately reflect the actual notification types being unregistered by the
unregister_notification_type calls that follow, so the comment correctly
documents why these specific unregistrations are needed.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@openwisp_controller/connection/tests/test_notifications.py`:
- Around line 51-57: The comment in the block starting with "Simulates if
'default notification type is already unregistered" incorrectly references the
notification types config_error and device_registered, but the actual code
unregisters connection_is_not_working and connection_is_working. Update the
comment text to accurately reflect the actual notification types being
unregistered by the unregister_notification_type calls that follow, so the
comment correctly documents why these specific unregistrations are needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 178858c5-93cc-451f-98de-c0ed95a0caf6

📥 Commits

Reviewing files that changed from the base of the PR and between e0ee749 and 9888b2c.

📒 Files selected for processing (18)
  • openwisp_controller/config/static/config/js/lib/jsonschema-ui.js
  • openwisp_controller/config/templates/admin/config/device/change_form.html
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/subnet_division/tests/test_models.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{py,html}

📄 CodeRabbit inference engine (Custom checks)

For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework

Files:

  • openwisp_controller/config/templates/admin/config/device/change_form.html
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}

📄 CodeRabbit inference engine (Custom checks)

**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously

Files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}

📄 CodeRabbit inference engine (Custom checks)

Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries

Files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it

Files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
🧠 Learnings (6)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.

Applied to files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.

Applied to files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.

Applied to files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.

Applied to files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.

Applied to files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.

Applied to files:

  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/geo/tests/test_api.py
🔇 Additional comments (16)
openwisp_controller/config/templates/admin/config/device/change_form.html (1)

20-20: LGTM!

openwisp_controller/connection/base/models.py (1)

110-110: LGTM!

Also applies to: 567-567

openwisp_controller/connection/commands.py (1)

61-61: LGTM!

openwisp_controller/connection/connectors/openwrt/ssh.py (1)

14-14: LGTM!

openwisp_controller/connection/connectors/ssh.py (1)

141-143: LGTM!

Also applies to: 190-195

openwisp_controller/subnet_division/base/models.py (1)

135-139: LGTM!

openwisp_controller/subnet_division/rule_types/base.py (1)

177-180: LGTM!

openwisp_controller/config/tests/test_admin.py (1)

1318-1320: LGTM!

openwisp_controller/config/tests/test_config.py (1)

105-120: LGTM!

openwisp_controller/geo/tests/test_api.py (1)

183-200: LGTM!

openwisp_controller/subnet_division/tests/test_models.py (1)

179-187: LGTM!

Also applies to: 281-284, 317-320, 498-504

openwisp_controller/config/tests/test_selenium.py (1)

330-334: LGTM!

Also applies to: 367-371

openwisp_controller/config/tests/test_views.py (1)

308-309: LGTM!

openwisp_controller/connection/tests/test_selenium.py (1)

58-61: LGTM!

openwisp_controller/connection/tests/utils.py (1)

19-21: LGTM!

openwisp_controller/geo/tests/pytest.py (1)

163-164: LGTM!

Also applies to: 184-185

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 14, 2026
@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (2/3).

@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (3/3).

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 14, 2026
@openwisp-companion

Copy link
Copy Markdown

Commit Message Format Failure

Hello @ApurveKaranwal,
(Analysis for commit 7d5c136)

The commit message is not in the required format. Please ensure your commit messages follow the Conventional Commits specification, including a type prefix, a capitalized title, and an optional issue reference.

Example of a correct commit message:

[test] Fix TypeError on NoneType returned by get_browser_logs() #123

This commit addresses an issue where get_browser_logs() could return None,
leading to a TypeError. The function has been updated to handle this case
gracefully.

Fixes #123

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openwisp_controller/subnet_division/base/models.py (1)

131-141: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Allow the exact-fit case here.

>= available rejects configurations that exactly fill the remaining subnet slots after reserving one subnet, even though the validation message implies only over-capacity cases should fail. Change this to > so the maximum valid request is accepted.

Suggested fix
-        if self.number_of_subnets >= available:
+        if self.number_of_subnets > available:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_controller/subnet_division/base/models.py` around lines 131 - 141,
The validation check for number_of_subnets uses the >= operator which
incorrectly rejects configurations that exactly match the available subnet slots
after reservation. Change the comparison operator from >= to > in the condition
`if self.number_of_subnets >= available:` so that exact-fit cases are allowed
and only truly over-capacity configurations are rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@openwisp_controller/subnet_division/base/models.py`:
- Around line 131-141: The validation check for number_of_subnets uses the >=
operator which incorrectly rejects configurations that exactly match the
available subnet slots after reservation. Change the comparison operator from >=
to > in the condition `if self.number_of_subnets >= available:` so that
exact-fit cases are allowed and only truly over-capacity configurations are
rejected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c7f10de2-cf01-40ee-b5d5-88d114c5d5e6

📥 Commits

Reviewing files that changed from the base of the PR and between b40eaab and 04c2b46.

📒 Files selected for processing (21)
  • openwisp_controller/config/static/config/js/lib/jsonschema-ui.js
  • openwisp_controller/config/templates/admin/config/device/change_form.html
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/tests/test_selenium.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}

📄 CodeRabbit inference engine (Custom checks)

**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously

Files:

  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}

📄 CodeRabbit inference engine (Custom checks)

Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries

Files:

  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
**/*.{py,html}

📄 CodeRabbit inference engine (Custom checks)

For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework

Files:

  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/config/templates/admin/config/device/change_form.html
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it

Files:

  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
🧠 Learnings (7)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.

Applied to files:

  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.

Applied to files:

  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.

Applied to files:

  • openwisp_controller/connection/connectors/openwrt/ssh.py
  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/commands.py
  • openwisp_controller/subnet_division/base/models.py
  • openwisp_controller/connection/base/models.py
  • openwisp_controller/subnet_division/rule_types/base.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/connection/connectors/ssh.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.

Applied to files:

  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.

Applied to files:

  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/connection/tests/utils.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/pytest.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.

Applied to files:

  • openwisp_controller/connection/tests/test_notifications.py
  • openwisp_controller/tests/test_selenium.py
  • openwisp_controller/connection/tests/test_selenium.py
  • openwisp_controller/config/whois/tests/tests.py
  • openwisp_controller/config/tests/test_config.py
  • openwisp_controller/config/tests/test_views.py
  • openwisp_controller/config/tests/test_admin.py
  • openwisp_controller/subnet_division/tests/test_models.py
  • openwisp_controller/geo/tests/test_api.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/config/tests/test_selenium.py
📚 Learning: 2026-03-27T20:50:26.240Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1315
File: openwisp_controller/geo/estimated_location/service.py:70-76
Timestamp: 2026-03-27T20:50:26.240Z
Learning: In openwisp-controller’s WHOIS and estimated-location services (openwisp_controller/config/whois/ and openwisp_controller/geo/estimated_location/), these components only process public IP addresses. When reviewing logs/error/debug messages in this area, treat logging the IP address as acceptable and do not flag it as a privacy/security concern—unless the logged value can originate from non-public/private IPs in that specific code path.

Applied to files:

  • openwisp_controller/config/whois/tests/tests.py
🔇 Additional comments (21)
openwisp_controller/config/whois/tests/tests.py (1)

1181-1181: LGTM!

openwisp_controller/geo/tests/test_selenium.py (3)

111-111: LGTM!


115-122: LGTM!


123-123: LGTM!

openwisp_controller/tests/test_selenium.py (1)

92-92: LGTM!

openwisp_controller/config/templates/admin/config/device/change_form.html (1)

20-20: LGTM!

openwisp_controller/config/tests/test_selenium.py (1)

330-334: LGTM!

Also applies to: 633-654

openwisp_controller/connection/tests/test_notifications.py (1)

55-55: LGTM!

openwisp_controller/connection/tests/test_selenium.py (1)

58-60: LGTM!

openwisp_controller/geo/tests/test_api.py (1)

188-200: LGTM!

openwisp_controller/subnet_division/tests/test_models.py (1)

181-185: LGTM!

Also applies to: 282-284, 318-320, 498-504

openwisp_controller/connection/base/models.py (1)

110-110: LGTM!

Also applies to: 567-567

openwisp_controller/connection/commands.py (1)

61-62: LGTM!

openwisp_controller/connection/connectors/openwrt/ssh.py (1)

14-15: LGTM!

openwisp_controller/connection/connectors/ssh.py (1)

141-143: LGTM!

Also applies to: 190-201

openwisp_controller/subnet_division/rule_types/base.py (1)

180-180: LGTM!

openwisp_controller/config/tests/test_admin.py (1)

1318-1320: LGTM!

openwisp_controller/config/tests/test_config.py (1)

114-114: LGTM!

openwisp_controller/config/tests/test_views.py (1)

308-309: LGTM!

openwisp_controller/connection/tests/utils.py (1)

19-21: LGTM!

openwisp_controller/geo/tests/pytest.py (1)

163-163: LGTM!

Also applies to: 184-184

@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 98.547%. remained the same — ApurveKaranwal:fix-typos into openwisp:master

@nemesifier nemesifier changed the title docs: fix various spelling mistakes and typos [docs] Fixed various spelling mistakes and typos Jun 16, 2026

@nemesifier nemesifier left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually I don't accept PRs like this one. I do not endorse this kind of AI usage, we risk getting hundred of similar PRs in different repos, which creates unnecessary burden for maintainers for almost zero value.

I will accept this one due to tue amount of typos found by the AI but please refrain from doing the same in all other repositories.

See my comment below.

@@ -3,7 +3,7 @@
* Released under the MIT license
**/
'use strict';
!function(){var a;!function(){var b=!1,c=/xyz/.test(function(){window.postMessage("xyz")})?/\b_super\b/:/.*/;return a=function(){},a.extend=function a(d){function e(){!b&&this.init&&this.init.apply(this,arguments)}var f=this.prototype;b=!0;var g=new this;b=!1;for(var h in d)g[h]="function"==typeof d[h]&&"function"==typeof f[h]&&c.test(d[h])?function(a,b){return function(){var c=this._super;this._super=f[a];var d=b.apply(this,arguments);return this._super=c,d}}(h,d[h]):d[h];return e.prototype=g,e.prototype.constructor=e,e.extend=a,e},a}(),function(){function a(a,b){b=b||{bubbles:!1,cancelable:!1,detail:void 0};var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,b.bubbles,b.cancelable,b.detail),c}a.prototype=window.Event.prototype,window.CustomEvent=a}(),function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;c<b.length&&!window.requestAnimationFrame;++c)window.requestAnimationFrame=window[b[c]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[b[c]+"CancelAnimationFrame"]||window[b[c]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(b,c){var d=(new Date).getTime(),e=Math.max(0,16-(d-a)),f=window.setTimeout(function(){b(d+e)},e);return a=d+e,f}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a)})}(),function(){Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)})}();var b=function(a){return!("object"!=typeof a||a.nodeType||null!==a&&a===a.window||a.constructor&&!Object.prototype.hasOwnProperty.call(a.constructor.prototype,"isPrototypeOf"))},c=function(a){var d,e,f;for(e=1;e<arguments.length;e++){d=arguments[e];for(f in d)d.hasOwnProperty(f)&&(d[f]&&b(d[f])?(a.hasOwnProperty(f)||(a[f]={}),c(a[f],d[f])):a[f]=d[f])}return a},d=function(a,b){if(a&&"object"==typeof a){var c;if(Array.isArray(a)||"number"==typeof a.length&&a.length>0&&a.length-1 in a){for(c=0;c<a.length;c++)if(b(c,a[c])===!1)return}else if(Object.keys){var d=Object.keys(a);for(c=0;c<d.length;c++)if(b(d[c],a[d[c]])===!1)return}else for(c in a)if(a.hasOwnProperty(c)&&b(c,a[c])===!1)return}},e=function(a,b){var c=document.createEvent("HTMLEvents");c.initEvent(b,!0,!0),a.dispatchEvent(c)},f=function(a,b){if(!(a instanceof Element))throw new Error("element should be an instance of Element");b=c({},f.defaults.options,b||{}),this.element=a,this.options=b,this.init()};f.prototype={constructor:f,init:function(){var a=this;this.ready=!1;var b=f.defaults.themes[this.options.theme||f.defaults.theme];if(!b)throw"Unknown theme "+(this.options.theme||f.defaults.theme);this.schema=this.options.schema,this.theme=new b,this.template=this.options.template,this.refs=this.options.refs||{},this.uuid=0,this.__data={};var c=f.defaults.iconlibs[this.options.iconlib||f.defaults.iconlib];c&&(this.iconlib=new c),this.root_container=this.theme.getContainer(),this.element.appendChild(this.root_container),this.translate=this.options.translate||f.defaults.translate,this._loadExternalRefs(this.schema,function(){a._getDefinitions(a.schema);var b={};a.options.custom_validators&&(b.custom_validators=a.options.custom_validators),a.validator=new f.Validator(a,null,b);var c=a.getEditorClass(a.schema);a.root=a.createEditor(c,{jsoneditor:a,schema:a.schema,required:!0,container:a.root_container}),a.root.preBuild(),a.root.build(),a.root.postBuild(),a.options.startval&&a.root.setValue(a.options.startval),a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.ready=!0,window.requestAnimationFrame(function(){a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.trigger("ready"),a.trigger("change"))})})},getValue:function(){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before getting the value";return this.root.getValue()},setValue:function(a){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before setting the value";return this.root.setValue(a),this},validate:function(a){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before validating";return 1===arguments.length?this.validator.validate(a):this.validation_results},destroy:function(){this.destroyed||this.ready&&(this.schema=null,this.options=null,this.root.destroy(),this.root=null,this.root_container=null,this.validator=null,this.validation_results=null,this.theme=null,this.iconlib=null,this.template=null,this.__data=null,this.ready=!1,this.element.innerHTML="",this.destroyed=!0)},on:function(a,b){return this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[],this.callbacks[a].push(b),this},off:function(a,b){if(a&&b){this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[];for(var c=[],d=0;d<this.callbacks[a].length;d++)this.callbacks[a][d]!==b&&c.push(this.callbacks[a][d]);this.callbacks[a]=c}else a?(this.callbacks=this.callbacks||{},this.callbacks[a]=[]):this.callbacks={};return this},trigger:function(a){if(this.callbacks&&this.callbacks[a]&&this.callbacks[a].length)for(var b=0;b<this.callbacks[a].length;b++)this.callbacks[a][b]();return this},setOption:function(a,b){if("show_errors"!==a)throw"Option "+a+" must be set during instantiation and cannot be changed later";return this.options.show_errors=b,this.onChange(),this},getEditorClass:function(a){var b;if(a=this.expandSchema(a),d(f.defaults.resolvers,function(c,d){var e=d(a);if(e&&f.defaults.editors[e])return b=e,!1}),!b)throw"Unknown editor for schema "+JSON.stringify(a);if(!f.defaults.editors[b])throw"Unknown editor "+b;return f.defaults.editors[b]},createEditor:function(a,b){return b=c({},a.options||{},b),new a(b)},onChange:function(){if(this.ready&&!this.firing_change){this.firing_change=!0;var a=this;return window.requestAnimationFrame(function(){a.firing_change=!1,a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),"never"!==a.options.show_errors?a.root.showValidationErrors(a.validation_results):a.root.showValidationErrors([]),a.trigger("change"))}),this}},compileTemplate:function(a,b){b=b||f.defaults.template;var c;if("string"==typeof b){if(!f.defaults.templates[b])throw"Unknown template engine "+b;if(c=f.defaults.templates[b](),!c)throw"Template engine "+b+" missing required library."}else c=b;if(!c)throw"No template engine set";if(!c.compile)throw"Invalid template engine set";return c.compile(a)},_data:function(a,b,c){if(3!==arguments.length)return a.hasAttribute("data-jsoneditor-"+b)?this.__data[a.getAttribute("data-jsoneditor-"+b)]:null;var d;a.hasAttribute("data-jsoneditor-"+b)?d=a.getAttribute("data-jsoneditor-"+b):(d=this.uuid++,a.setAttribute("data-jsoneditor-"+b,d)),this.__data[d]=c},registerEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=a,this},unregisterEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=null,this},getEditor:function(a){if(this.editors)return this.editors[a]},watch:function(a,b){return this.watchlist=this.watchlist||{},this.watchlist[a]=this.watchlist[a]||[],this.watchlist[a].push(b),this},unwatch:function(a,b){if(!this.watchlist||!this.watchlist[a])return this;if(!b)return this.watchlist[a]=null,this;for(var c=[],d=0;d<this.watchlist[a].length;d++)this.watchlist[a][d]!==b&&c.push(this.watchlist[a][d]);return this.watchlist[a]=c.length?c:null,this},notifyWatchers:function(a){if(!this.watchlist||!this.watchlist[a])return this;for(var b=0;b<this.watchlist[a].length;b++)this.watchlist[a][b]()},isEnabled:function(){return!this.root||this.root.isEnabled()},enable:function(){this.root.enable()},disable:function(){this.root.disable()},_getDefinitions:function(a,b){if(b=b||"#/definitions/",a.definitions)for(var c in a.definitions)a.definitions.hasOwnProperty(c)&&(this.refs[b+c]=a.definitions[c],a.definitions[c].definitions&&this._getDefinitions(a.definitions[c],b+c+"/definitions/"))},_getExternalRefs:function(a){var b={},c=function(a){for(var c in a)a.hasOwnProperty(c)&&(b[c]=!0)};a.$ref&&"object"!=typeof a.$ref&&"#"!==a.$ref.substr(0,1)&&!this.refs[a.$ref]&&(b[a.$ref]=!0);for(var d in a)if(a.hasOwnProperty(d))if(a[d]&&"object"==typeof a[d]&&Array.isArray(a[d]))for(var e=0;e<a[d].length;e++)"object"==typeof a[d][e]&&c(this._getExternalRefs(a[d][e]));else a[d]&&"object"==typeof a[d]&&c(this._getExternalRefs(a[d]));return b},_loadExternalRefs:function(a,b){var c=this,e=this._getExternalRefs(a),f=0,g=0,h=!1;d(e,function(a){if(!c.refs[a]){if(!c.options.ajax)throw"Must set ajax option to true to load external ref "+a;c.refs[a]="loading",g++;var d=new XMLHttpRequest;d.open("GET",a,!0),d.onreadystatechange=function(){if(4==d.readyState){if(200!==d.status)throw window.console.log(d),"Failed to fetch ref via ajax- "+a;var e;try{e=JSON.parse(d.responseText)}catch(b){throw window.console.log(b),"Failed to parse external ref "+a}if(!e||"object"!=typeof e)throw"External ref does not contain a valid schema - "+a;c.refs[a]=e,c._loadExternalRefs(e,function(){f++,f>=g&&!h&&(h=!0,b())})}},d.send()}}),g||b()},expandRefs:function(a){for(a=c({},a);a.$ref;){var b=a.$ref;delete a.$ref,this.refs[b]||(b=decodeURIComponent(b)),a=this.extendSchemas(a,this.refs[b])}return a},expandSchema:function(a){var b,e=this,f=c({},a);if("object"==typeof a.type&&(Array.isArray(a.type)?d(a.type,function(b,c){"object"==typeof c&&(a.type[b]=e.expandSchema(c))}):a.type=e.expandSchema(a.type)),"object"==typeof a.disallow&&(Array.isArray(a.disallow)?d(a.disallow,function(b,c){"object"==typeof c&&(a.disallow[b]=e.expandSchema(c))}):a.disallow=e.expandSchema(a.disallow)),a.anyOf&&d(a.anyOf,function(b,c){a.anyOf[b]=e.expandSchema(c)}),a.dependencies&&d(a.dependencies,function(b,c){"object"!=typeof c||Array.isArray(c)||(a.dependencies[b]=e.expandSchema(c))}),a.not&&(a.not=this.expandSchema(a.not)),a.allOf){for(b=0;b<a.allOf.length;b++)f=this.extendSchemas(f,this.expandSchema(a.allOf[b]));delete f.allOf}if(a.extends){if(Array.isArray(a.extends))for(b=0;b<a.extends.length;b++)f=this.extendSchemas(f,this.expandSchema(a.extends[b]));else f=this.extendSchemas(f,this.expandSchema(a.extends));delete f.extends}if(a.oneOf){var g=c({},f);for(delete g.oneOf,b=0;b<a.oneOf.length;b++)f.oneOf[b]=this.extendSchemas(this.expandSchema(a.oneOf[b]),g)}return this.expandRefs(f)},extendSchemas:function(a,b){a=c({},a),b=c({},b);var e=this,f={};return d(a,function(a,c){"undefined"!=typeof b[a]?"required"!==a&&"defaultProperties"!==a||"object"!=typeof c||!Array.isArray(c)?"type"!==a||"string"!=typeof c&&!Array.isArray(c)?"object"==typeof c&&Array.isArray(c)?f[a]=c.filter(function(c){return b[a].indexOf(c)!==-1}):"object"==typeof c&&null!==c?f[a]=e.extendSchemas(c,b[a]):f[a]=c:("string"==typeof c&&(c=[c]),"string"==typeof b.type&&(b.type=[b.type]),b.type&&b.type.length?f.type=c.filter(function(a){return b.type.indexOf(a)!==-1}):f.type=c,1===f.type.length&&"string"==typeof f.type[0]?f.type=f.type[0]:0===f.type.length&&delete f.type):f[a]=c.concat(b[a]).reduce(function(a,b){return a.indexOf(b)<0&&a.push(b),a},[]):f[a]=c}),d(b,function(b,c){"undefined"==typeof a[b]&&(f[b]=c)}),f}},f.defaults={themes:{},templates:{},iconlibs:{},editors:{},languages:{},resolvers:[],custom_validators:[]},f.Validator=a.extend({init:function(a,b,c){this.jsoneditor=a,this.schema=b||this.jsoneditor.schema,this.options=c||{},this.translate=this.jsoneditor.translate||f.defaults.translate},validate:function(a){return this._validateSchema(this.schema,a)},_validateSchema:function(a,b,e){var g,h,i,j=this,k=[],l=JSON.stringify(b);if(e=e||"root",a=c({},this.jsoneditor.expandRefs(a)),a.required&&a.required===!0){if("undefined"==typeof b)return k.push({path:e,property:"required",message:this.translate("error_notset")}),k}else if("undefined"==typeof b){if(!this.jsoneditor.options.required_by_default)return k;k.push({path:e,property:"required",message:this.translate("error_notset")})}if(a.enum){for(g=!1,h=0;h<a.enum.length;h++)l===JSON.stringify(a.enum[h])&&(g=!0);g||k.push({path:e,property:"enum",message:this.translate("error_enum")})}if(a.extends)for(h=0;h<a.extends.length;h++)k=k.concat(this._validateSchema(a.extends[h],b,e));if(a.allOf)for(h=0;h<a.allOf.length;h++)k=k.concat(this._validateSchema(a.allOf[h],b,e));if(a.anyOf){for(g=!1,h=0;h<a.anyOf.length;h++)if(!this._validateSchema(a.anyOf[h],b,e).length){g=!0;break}g||k.push({path:e,property:"anyOf",message:this.translate("error_anyOf")})}if(a.oneOf){g=0;var m=[];for(h=0;h<a.oneOf.length;h++){var n=this._validateSchema(a.oneOf[h],b,e);for(n.length||g++,i=0;i<n.length;i++)n[i].path=e+".oneOf["+h+"]"+n[i].path.substr(e.length);m=m.concat(n)}1!==g&&(k.push({path:e,property:"oneOf",message:this.translate("error_oneOf",[g])}),k=k.concat(m))}if(a.not&&(this._validateSchema(a.not,b,e).length||k.push({path:e,property:"not",message:this.translate("error_not")})),a.type)if(Array.isArray(a.type)){for(g=!1,h=0;h<a.type.length;h++)if(this._checkType(a.type[h],b)){g=!0;break}g||k.push({path:e,property:"type",message:this.translate("error_type_union")})}else this._checkType(a.type,b)||k.push({path:e,property:"type",message:this.translate("error_type",[a.type])});if(a.disallow)if(Array.isArray(a.disallow)){for(g=!0,h=0;h<a.disallow.length;h++)if(this._checkType(a.disallow[h],b)){g=!1;break}g||k.push({path:e,property:"disallow",message:this.translate("error_disallow_union")})}else this._checkType(a.disallow,b)&&k.push({path:e,property:"disallow",message:this.translate("error_disallow",[a.disallow])});if("number"==typeof b){if(a.multipleOf||a.divisibleBy){var o=a.multipleOf||a.divisibleBy;g=b/o===Math.floor(b/o),window.math?g=window.math.mod(window.math.bignumber(b),window.math.bignumber(o)).equals(0):window.Decimal&&(g=new window.Decimal(b).mod(new window.Decimal(o)).equals(0)),g||k.push({path:e,property:a.multipleOf?"multipleOf":"divisibleBy",message:this.translate("error_multipleOf",[o])})}a.hasOwnProperty("maximum")&&(g=a.exclusiveMaximum?b<a.maximum:b<=a.maximum,window.math?g=window.math[a.exclusiveMaximum?"smaller":"smallerEq"](window.math.bignumber(b),window.math.bignumber(a.maximum)):window.Decimal&&(g=new window.Decimal(b)[a.exclusiveMaximum?"lt":"lte"](new window.Decimal(a.maximum))),g||k.push({path:e,property:"maximum",message:this.translate(a.exclusiveMaximum?"error_maximum_excl":"error_maximum_incl",[a.maximum])})),a.hasOwnProperty("minimum")&&(g=a.exclusiveMinimum?b>a.minimum:b>=a.minimum,window.math?g=window.math[a.exclusiveMinimum?"larger":"largerEq"](window.math.bignumber(b),window.math.bignumber(a.minimum)):window.Decimal&&(g=new window.Decimal(b)[a.exclusiveMinimum?"gt":"gte"](new window.Decimal(a.minimum))),g||k.push({path:e,property:"minimum",message:this.translate(a.exclusiveMinimum?"error_minimum_excl":"error_minimum_incl",[a.minimum])}))}else if("string"==typeof b)a.maxLength&&(b+"").length>a.maxLength&&k.push({path:e,property:"maxLength",message:this.translate("error_maxLength",[a.maxLength])}),a.minLength&&(b+"").length<a.minLength&&k.push({path:e,property:"minLength",message:this.translate(1===a.minLength?"error_notempty":"error_minLength",[a.minLength])}),a.pattern&&(new RegExp(a.pattern).test(b)||k.push({path:e,property:"pattern",message:this.translate("error_pattern",[a.pattern])}));else if("object"==typeof b&&null!==b&&Array.isArray(b)){if(a.items)if(Array.isArray(a.items))for(h=0;h<b.length;h++)if(a.items[h])k=k.concat(this._validateSchema(a.items[h],b[h],e+"."+h));else{if(a.additionalItems===!0)break;if(!a.additionalItems){if(a.additionalItems===!1){k.push({path:e,property:"additionalItems",message:this.translate("error_additionalItems")});break}break}k=k.concat(this._validateSchema(a.additionalItems,b[h],e+"."+h))}else for(h=0;h<b.length;h++)k=k.concat(this._validateSchema(a.items,b[h],e+"."+h));if(a.maxItems&&b.length>a.maxItems&&k.push({path:e,property:"maxItems",message:this.translate("error_maxItems",[a.maxItems])}),a.minItems&&b.length<a.minItems&&k.push({path:e,property:"minItems",message:this.translate("error_minItems",[a.minItems])}),a.uniqueItems){var p={};for(h=0;h<b.length;h++){if(g=JSON.stringify(b[h]),p[g]){k.push({path:e,property:"uniqueItems",message:this.translate("error_uniqueItems")});break}p[g]=!0}}}else if("object"==typeof b&&null!==b){if(a.maxProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g>a.maxProperties&&k.push({path:e,property:"maxProperties",message:this.translate("error_maxProperties",[a.maxProperties])})}if(a.minProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g<a.minProperties&&k.push({path:e,property:"minProperties",message:this.translate("error_minProperties",[a.minProperties])})}if(a.required&&Array.isArray(a.required))for(h=0;h<a.required.length;h++)"undefined"==typeof b[a.required[h]]&&k.push({path:e,property:"required",message:this.translate("error_required",[a.required[h]])});var q={};if(a.properties)for(h in a.properties)a.properties.hasOwnProperty(h)&&(q[h]=!0,k=k.concat(this._validateSchema(a.properties[h],b[h],e+"."+h)));if(a.patternProperties)for(h in a.patternProperties)if(a.patternProperties.hasOwnProperty(h)){var r=new RegExp(h);for(i in b)b.hasOwnProperty(i)&&r.test(i)&&(q[i]=!0,k=k.concat(this._validateSchema(a.patternProperties[h],b[i],e+"."+i)))}if("undefined"!=typeof a.additionalProperties||!this.jsoneditor.options.no_additional_properties||a.oneOf||a.anyOf||(a.additionalProperties=!1),"undefined"!=typeof a.additionalProperties)for(h in b)if(b.hasOwnProperty(h)&&!q[h]){if(!a.additionalProperties){k.push({path:e,property:"additionalProperties",message:this.translate("error_additional_properties",[h])});break}if(a.additionalProperties===!0)break;k=k.concat(this._validateSchema(a.additionalProperties,b[h],e+"."+h))}if(a.dependencies)for(h in a.dependencies)if(a.dependencies.hasOwnProperty(h)&&"undefined"!=typeof b[h])if(Array.isArray(a.dependencies[h]))for(i=0;i<a.dependencies[h].length;i++)"undefined"==typeof b[a.dependencies[h][i]]&&k.push({path:e,property:"dependencies",message:this.translate("error_dependency",[a.dependencies[h][i]])});else k=k.concat(this._validateSchema(a.dependencies[h],b,e))}return d(f.defaults.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),this.options.custom_validators&&d(this.options.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),k},_checkType:function(a,b){return"string"==typeof a?"string"===a?"string"==typeof b:"number"===a?"number"==typeof b:"integer"===a?"number"==typeof b&&b===Math.floor(b):"boolean"===a?"boolean"==typeof b:"array"===a?Array.isArray(b):"object"===a?null!==b&&!Array.isArray(b)&&"object"==typeof b:"null"!==a||null===b:!this._validateSchema(a,b).length}}),f.AbstractEditor=a.extend({onChildEditorChange:function(a){this.onChange(!0)},notify:function(){this.jsoneditor.notifyWatchers(this.path)},change:function(){this.parent?this.parent.onChildEditorChange(this):this.jsoneditor.onChange()},onChange:function(a){this.notify(),this.watch_listener&&this.watch_listener(),a&&this.change()},register:function(){this.jsoneditor.registerEditor(this),this.onChange()},unregister:function(){this.jsoneditor&&this.jsoneditor.unregisterEditor(this)},getNumColumns:function(){return 12},init:function(a){this.jsoneditor=a.jsoneditor,this.theme=this.jsoneditor.theme,this.template_engine=this.jsoneditor.template,this.iconlib=this.jsoneditor.iconlib,this.translate=this.jsoneditor.translate||f.defaults.translate,this.original_schema=a.schema,this.schema=this.jsoneditor.expandSchema(this.original_schema),this.options=c({},this.options||{},a.schema.options||{},a),a.path||this.schema.id||(this.schema.id="root"),this.path=a.path||"root",this.formname=a.formname||this.path.replace(/\.([^.]+)/g,"[$1]"),this.jsoneditor.options.form_name_root&&(this.formname=this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+"[")),this.key=this.path.split(".").pop(),this.parent=a.parent,this.link_watchers=[],a.container&&this.setContainer(a.container)},setContainer:function(a){this.container=a,this.schema.id&&this.container.setAttribute("data-schemaid",this.schema.id),this.schema.type&&"string"==typeof this.schema.type&&this.container.setAttribute("data-schematype",this.schema.type),this.container.setAttribute("data-schemapath",this.path)},preBuild:function(){},build:function(){},postBuild:function(){this.setupWatchListeners(),this.addLinks(),this.setValue(this.getDefault(),!0),this.updateHeaderText(),this.register(),this.onWatchedFieldChange()},setupWatchListeners:function(){var a=this;if(this.watched={},this.schema.vars&&(this.schema.watch=this.schema.vars),this.watched_values={},this.watch_listener=function(){a.refreshWatchedFieldValues()&&a.onWatchedFieldChange()},this.register(),this.schema.hasOwnProperty("watch")){var b,c,d,e,f;for(var g in this.schema.watch)if(this.schema.watch.hasOwnProperty(g)){if(b=this.schema.watch[g],Array.isArray(b)){if(b.length<2)continue;c=[b[0]].concat(b[1].split("."))}else c=b.split("."),a.theme.closest(a.container,'[data-schemaid="'+c[0]+'"]')||c.unshift("#");if(d=c.shift(),"#"===d&&(d=a.jsoneditor.schema.id||"root"),e=a.theme.closest(a.container,'[data-schemaid="'+d+'"]'),!e)throw"Could not find ancestor node with id "+d;f=e.getAttribute("data-schemapath")+"."+c.join("."),a.jsoneditor.watch(f,a.watch_listener),a.watched[g]=f}}this.schema.headerTemplate&&(this.header_template=this.jsoneditor.compileTemplate(this.schema.headerTemplate,this.template_engine))},addLinks:function(){if(!this.no_link_holder&&(this.link_holder=this.theme.getLinksHolder(),this.container.appendChild(this.link_holder),this.schema.links))for(var a=0;a<this.schema.links.length;a++)this.addLink(this.getLink(this.schema.links[a]))},getButton:function(a,b,c){var d="json-editor-btn-"+b;b=this.iconlib?this.iconlib.getIcon(b):null,!b&&c&&(a=c,c=null);var e=this.theme.getButton(a,b,c);return e.className+=" "+d+" ",e},setButtonText:function(a,b,c,d){return c=this.iconlib?this.iconlib.getIcon(c):null,!c&&d&&(b=d,d=null),this.theme.setButtonText(a,b,c,d)},addLink:function(a){this.link_holder&&this.link_holder.appendChild(a)},getLink:function(a){var b,c,d=a.mediaType||"application/javascript",e=d.split("/")[0],f=this.jsoneditor.compileTemplate(a.href,this.template_engine),g=null;if(a.download&&(g=a.download),g&&g!==!0&&(g=this.jsoneditor.compileTemplate(g,this.template_engine)),"image"===e){b=this.theme.getBlockLinkHolder(),c=document.createElement("a"),c.setAttribute("target","_blank");var h=document.createElement("img");this.theme.createImageLink(b,c,h),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.setAttribute("title",a.rel||d),h.setAttribute("src",d)})}else if(["audio","video"].indexOf(e)>=0){b=this.theme.getBlockLinkHolder(),c=this.theme.getBlockLink(),c.setAttribute("target","_blank");var i=document.createElement(e);i.setAttribute("controls","controls"),this.theme.createMediaLink(b,c,i),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.textContent=a.rel||d,i.setAttribute("src",d)})}else c=b=this.theme.getBlockLink(),b.setAttribute("target","_blank"),b.textContent=a.rel,this.link_watchers.push(function(c){var d=f(c);b.setAttribute("href",d),b.textContent=a.rel||d});return g&&c&&(g===!0?c.setAttribute("download",""):this.link_watchers.push(function(a){c.setAttribute("download",g(a))})),a.class&&(c.className=c.className+" "+a.class),b},refreshWatchedFieldValues:function(){if(this.watched_values){var a={},b=!1,c=this;if(this.watched){var d,e;for(var f in this.watched)this.watched.hasOwnProperty(f)&&(e=c.jsoneditor.getEditor(this.watched[f]),d=e?e.getValue():null,c.watched_values[f]!==d&&(b=!0),a[f]=d)}return a.self=this.getValue(),this.watched_values.self!==a.self&&(b=!0),this.watched_values=a,b}},getWatchedFieldValues:function(){return this.watched_values},updateHeaderText:function(){if(this.header)if(this.header.children.length){for(var a=0;a<this.header.childNodes.length;a++)if(3===this.header.childNodes[a].nodeType){this.header.childNodes[a].nodeValue=this.getHeaderText();break}}else this.header.textContent=this.getHeaderText()},getHeaderText:function(a){return this.header_text?this.header_text:a?this.schema.title:this.getTitle()},onWatchedFieldChange:function(){var a;if(this.header_template){a=c(this.getWatchedFieldValues(),{key:this.key,i:this.key,i0:1*this.key,i1:1*this.key+1,title:this.getTitle()});var b=this.header_template(a);b!==this.header_text&&(this.header_text=b,this.updateHeaderText(),this.notify())}if(this.link_watchers.length){a=this.getWatchedFieldValues();for(var d=0;d<this.link_watchers.length;d++)this.link_watchers[d](a)}},setValue:function(a){this.value=a},getValue:function(){return this.value},refreshValue:function(){},getChildEditors:function(){return!1},destroy:function(){var a=this;this.unregister(this),d(this.watched,function(b,c){a.jsoneditor.unwatch(c,a.watch_listener)}),this.watched=null,this.watched_values=null,this.watch_listener=null,this.header_text=null,this.header_template=null,this.value=null,this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container),this.container=null,this.jsoneditor=null,this.schema=null,this.path=null,this.key=null,this.parent=null},getDefault:function(){if(this.schema.default)return this.schema.default;if(this.schema.enum)return this.schema.enum[0];var a=this.schema.type||this.schema.oneOf;if(a&&Array.isArray(a)&&(a=a[0]),a&&"object"==typeof a&&(a=a.type),a&&Array.isArray(a)&&(a=a[0]),"string"==typeof a){if("number"===a)return 0;if("boolean"===a)return!1;if("integer"===a)return 0;if("string"===a)return"";if("object"===a)return{};if("array"===a)return[]}return null},getTitle:function(){return this.schema.title||this.key},enable:function(){this.disabled=!1},disable:function(){this.disabled=!0},isEnabled:function(){return!this.disabled},isRequired:function(){return"boolean"==typeof this.schema.required?this.schema.required:this.parent&&this.parent.schema&&Array.isArray(this.parent.schema.required)?this.parent.schema.required.indexOf(this.key)>-1:!!this.jsoneditor.options.required_by_default},getDisplayText:function(a){var b=[],c={};d(a,function(a,b){b.title&&(c[b.title]=c[b.title]||0,c[b.title]++),b.description&&(c[b.description]=c[b.description]||0,c[b.description]++),b.format&&(c[b.format]=c[b.format]||0,c[b.format]++),b.type&&(c[b.type]=c[b.type]||0,c[b.type]++)}),d(a,function(a,d){var e;e="string"==typeof d?d:d.title&&c[d.title]<=1?d.title:d.format&&c[d.format]<=1?d.format:d.type&&c[d.type]<=1?d.type:d.description&&c[d.description]<=1?d.descripton:d.title?d.title:d.format?d.format:d.type?d.type:d.description?d.description:JSON.stringify(d).length<50?JSON.stringify(d):"type",b.push(e)});var e={};return d(b,function(a,d){e[d]=e[d]||0,e[d]++,c[d]>1&&(b[a]=d+" "+e[d])}),b},getOption:function(a){try{throw"getOption is deprecated"}catch(a){window.console.error(a)}return this.options[a]},showValidationErrors:function(a){}}),f.defaults.editors.null=f.AbstractEditor.extend({getValue:function(){return null},setValue:function(){this.onChange()},getNumColumns:function(){return 2}}),f.defaults.editors.string=f.AbstractEditor.extend({register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},setValue:function(a,b,c){if((!this.template||c)&&(null===a||"undefined"==typeof a?a="":"object"==typeof a?a=JSON.stringify(a):"string"!=typeof a&&(a=""+a),a!==this.serialized)){var d=this.sanitize(a);if(this.input.value!==d){this.input.value=d,this.sceditor_instance?this.sceditor_instance.val(d):this.epiceditor?this.epiceditor.importFile(null,d):this.ace_editor&&this.ace_editor.setValue(d);var e=c||this.getValue()!==a;this.refreshValue(),b?this.is_dirty=!1:"change"===this.jsoneditor.options.show_errors&&(this.is_dirty=!0),this.adjust_height&&this.adjust_height(this.input),this.onChange(e)}}},getNumColumns:function(){var a,b=Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);return a="textarea"===this.input_type?6:["text","email"].indexOf(this.input_type)>=0?4:2,Math.min(12,Math.max(b,a))},build:function(){var a=this;if(this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.format=this.schema.format,!this.format&&this.schema.media&&this.schema.media.type&&(this.format=this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,"")),!this.format&&this.options.default_format&&(this.format=this.options.default_format),this.options.format&&(this.format=this.options.format),this.format)if("textarea"===this.format)this.input_type="textarea",this.input=this.theme.getTextareaInput();else if("range"===this.format){this.input_type="range";var b=this.schema.minimum||0,c=this.schema.maximum||Math.max(100,b+1),d=1;this.schema.multipleOf&&(b%this.schema.multipleOf&&(b=Math.ceil(b/this.schema.multipleOf)*this.schema.multipleOf),c%this.schema.multipleOf&&(c=Math.floor(c/this.schema.multipleOf)*this.schema.multipleOf),d=this.schema.multipleOf),this.input=this.theme.getRangeInput(b,c,d)}else["actionscript","batchfile","bbcode","c","c++","cpp","coffee","csharp","css","dart","django","ejs","erlang","golang","groovy","handlebars","haskell","haxe","html","ini","jade","java","javascript","json","less","lisp","lua","makefile","markdown","matlab","mysql","objectivec","pascal","perl","pgsql","php","python","r","ruby","sass","scala","scss","smarty","sql","stylus","svg","twig","vbscript","xml","yaml"].indexOf(this.format)>=0?(this.input_type=this.format,this.source_code=!0,this.input=this.theme.getTextareaInput()):(this.input_type=this.format,this.input=this.theme.getFormInputField(this.input_type));else this.input_type="text",this.input=this.theme.getFormInputField(this.input_type);"undefined"!=typeof this.schema.maxLength&&this.input.setAttribute("maxlength",this.schema.maxLength),"undefined"!=typeof this.schema.pattern?this.input.setAttribute("pattern",this.schema.pattern):"undefined"!=typeof this.schema.minLength&&this.input.setAttribute("pattern",".{"+this.schema.minLength+",}"),this.options.compact?this.container.className+=" compact":this.options.input_width&&(this.input.style.width=this.options.input_width),(this.schema.readOnly||this.schema.readonly||this.schema.template)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),a.schema.template)return void(this.value=a.value);var c=this.value,d=a.sanitize(c);c!==d&&(this.value=d),a.is_dirty=!0,a.refreshValue(),a.onChange(!0)}),this.options.input_height&&(this.input.style.height=this.options.input_height),this.options.expand_height&&(this.adjust_height=function(a){if(a){var b,c=a.offsetHeight;if(a.offsetHeight<a.scrollHeight)for(b=0;a.offsetHeight<a.scrollHeight+3&&!(b>100);)b++,c++,a.style.height=c+"px";else{for(b=0;a.offsetHeight>=a.scrollHeight+3&&!(b>100);)b++,c--,a.style.height=c+"px";a.style.height=c+1+"px"}}},this.input.addEventListener("keyup",function(b){a.adjust_height(this)}),this.input.addEventListener("change",function(b){a.adjust_height(this)}),this.adjust_height()),this.format&&this.input.setAttribute("data-schemaformat",this.format),this.control=this.theme.getFormControl(this.label,this.input,this.description),this.container.appendChild(this.control),window.requestAnimationFrame(function(){a.input.parentNode&&a.afterInputReady(),a.adjust_height&&a.adjust_height(a.input)}),this.schema.template?(this.template=this.jsoneditor.compileTemplate(this.schema.template,this.template_engine),this.refreshValue()):this.refreshValue()},enable:function(){this.always_disabled||(this.input.disabled=!1),this._super()},disable:function(){this.input.disabled=!0,this._super()},afterInputReady:function(){var a,b=this;if(this.source_code)if(this.options.wysiwyg&&["html","bbcode"].indexOf(this.input_type)>=0&&window.jQuery&&window.jQuery.fn&&window.jQuery.fn.sceditor)a=c({},{
!function(){var a;!function(){var b=!1,c=/xyz/.test(function(){window.postMessage("xyz")})?/\b_super\b/:/.*/;return a=function(){},a.extend=function a(d){function e(){!b&&this.init&&this.init.apply(this,arguments)}var f=this.prototype;b=!0;var g=new this;b=!1;for(var h in d)g[h]="function"==typeof d[h]&&"function"==typeof f[h]&&c.test(d[h])?function(a,b){return function(){var c=this._super;this._super=f[a];var d=b.apply(this,arguments);return this._super=c,d}}(h,d[h]):d[h];return e.prototype=g,e.prototype.constructor=e,e.extend=a,e},a}(),function(){function a(a,b){b=b||{bubbles:!1,cancelable:!1,detail:void 0};var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,b.bubbles,b.cancelable,b.detail),c}a.prototype=window.Event.prototype,window.CustomEvent=a}(),function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;c<b.length&&!window.requestAnimationFrame;++c)window.requestAnimationFrame=window[b[c]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[b[c]+"CancelAnimationFrame"]||window[b[c]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(b,c){var d=(new Date).getTime(),e=Math.max(0,16-(d-a)),f=window.setTimeout(function(){b(d+e)},e);return a=d+e,f}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a)})}(),function(){Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)})}();var b=function(a){return!("object"!=typeof a||a.nodeType||null!==a&&a===a.window||a.constructor&&!Object.prototype.hasOwnProperty.call(a.constructor.prototype,"isPrototypeOf"))},c=function(a){var d,e,f;for(e=1;e<arguments.length;e++){d=arguments[e];for(f in d)d.hasOwnProperty(f)&&(d[f]&&b(d[f])?(a.hasOwnProperty(f)||(a[f]={}),c(a[f],d[f])):a[f]=d[f])}return a},d=function(a,b){if(a&&"object"==typeof a){var c;if(Array.isArray(a)||"number"==typeof a.length&&a.length>0&&a.length-1 in a){for(c=0;c<a.length;c++)if(b(c,a[c])===!1)return}else if(Object.keys){var d=Object.keys(a);for(c=0;c<d.length;c++)if(b(d[c],a[d[c]])===!1)return}else for(c in a)if(a.hasOwnProperty(c)&&b(c,a[c])===!1)return}},e=function(a,b){var c=document.createEvent("HTMLEvents");c.initEvent(b,!0,!0),a.dispatchEvent(c)},f=function(a,b){if(!(a instanceof Element))throw new Error("element should be an instance of Element");b=c({},f.defaults.options,b||{}),this.element=a,this.options=b,this.init()};f.prototype={constructor:f,init:function(){var a=this;this.ready=!1;var b=f.defaults.themes[this.options.theme||f.defaults.theme];if(!b)throw"Unknown theme "+(this.options.theme||f.defaults.theme);this.schema=this.options.schema,this.theme=new b,this.template=this.options.template,this.refs=this.options.refs||{},this.uuid=0,this.__data={};var c=f.defaults.iconlibs[this.options.iconlib||f.defaults.iconlib];c&&(this.iconlib=new c),this.root_container=this.theme.getContainer(),this.element.appendChild(this.root_container),this.translate=this.options.translate||f.defaults.translate,this._loadExternalRefs(this.schema,function(){a._getDefinitions(a.schema);var b={};a.options.custom_validators&&(b.custom_validators=a.options.custom_validators),a.validator=new f.Validator(a,null,b);var c=a.getEditorClass(a.schema);a.root=a.createEditor(c,{jsoneditor:a,schema:a.schema,required:!0,container:a.root_container}),a.root.preBuild(),a.root.build(),a.root.postBuild(),a.options.startval&&a.root.setValue(a.options.startval),a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.ready=!0,window.requestAnimationFrame(function(){a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.trigger("ready"),a.trigger("change"))})})},getValue:function(){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before getting the value";return this.root.getValue()},setValue:function(a){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before setting the value";return this.root.setValue(a),this},validate:function(a){if(!this.ready)throw"JSON Editor not ready yet. Listen for 'ready' event before validating";return 1===arguments.length?this.validator.validate(a):this.validation_results},destroy:function(){this.destroyed||this.ready&&(this.schema=null,this.options=null,this.root.destroy(),this.root=null,this.root_container=null,this.validator=null,this.validation_results=null,this.theme=null,this.iconlib=null,this.template=null,this.__data=null,this.ready=!1,this.element.innerHTML="",this.destroyed=!0)},on:function(a,b){return this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[],this.callbacks[a].push(b),this},off:function(a,b){if(a&&b){this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[];for(var c=[],d=0;d<this.callbacks[a].length;d++)this.callbacks[a][d]!==b&&c.push(this.callbacks[a][d]);this.callbacks[a]=c}else a?(this.callbacks=this.callbacks||{},this.callbacks[a]=[]):this.callbacks={};return this},trigger:function(a){if(this.callbacks&&this.callbacks[a]&&this.callbacks[a].length)for(var b=0;b<this.callbacks[a].length;b++)this.callbacks[a][b]();return this},setOption:function(a,b){if("show_errors"!==a)throw"Option "+a+" must be set during instantiation and cannot be changed later";return this.options.show_errors=b,this.onChange(),this},getEditorClass:function(a){var b;if(a=this.expandSchema(a),d(f.defaults.resolvers,function(c,d){var e=d(a);if(e&&f.defaults.editors[e])return b=e,!1}),!b)throw"Unknown editor for schema "+JSON.stringify(a);if(!f.defaults.editors[b])throw"Unknown editor "+b;return f.defaults.editors[b]},createEditor:function(a,b){return b=c({},a.options||{},b),new a(b)},onChange:function(){if(this.ready&&!this.firing_change){this.firing_change=!0;var a=this;return window.requestAnimationFrame(function(){a.firing_change=!1,a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),"never"!==a.options.show_errors?a.root.showValidationErrors(a.validation_results):a.root.showValidationErrors([]),a.trigger("change"))}),this}},compileTemplate:function(a,b){b=b||f.defaults.template;var c;if("string"==typeof b){if(!f.defaults.templates[b])throw"Unknown template engine "+b;if(c=f.defaults.templates[b](),!c)throw"Template engine "+b+" missing required library."}else c=b;if(!c)throw"No template engine set";if(!c.compile)throw"Invalid template engine set";return c.compile(a)},_data:function(a,b,c){if(3!==arguments.length)return a.hasAttribute("data-jsoneditor-"+b)?this.__data[a.getAttribute("data-jsoneditor-"+b)]:null;var d;a.hasAttribute("data-jsoneditor-"+b)?d=a.getAttribute("data-jsoneditor-"+b):(d=this.uuid++,a.setAttribute("data-jsoneditor-"+b,d)),this.__data[d]=c},registerEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=a,this},unregisterEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=null,this},getEditor:function(a){if(this.editors)return this.editors[a]},watch:function(a,b){return this.watchlist=this.watchlist||{},this.watchlist[a]=this.watchlist[a]||[],this.watchlist[a].push(b),this},unwatch:function(a,b){if(!this.watchlist||!this.watchlist[a])return this;if(!b)return this.watchlist[a]=null,this;for(var c=[],d=0;d<this.watchlist[a].length;d++)this.watchlist[a][d]!==b&&c.push(this.watchlist[a][d]);return this.watchlist[a]=c.length?c:null,this},notifyWatchers:function(a){if(!this.watchlist||!this.watchlist[a])return this;for(var b=0;b<this.watchlist[a].length;b++)this.watchlist[a][b]()},isEnabled:function(){return!this.root||this.root.isEnabled()},enable:function(){this.root.enable()},disable:function(){this.root.disable()},_getDefinitions:function(a,b){if(b=b||"#/definitions/",a.definitions)for(var c in a.definitions)a.definitions.hasOwnProperty(c)&&(this.refs[b+c]=a.definitions[c],a.definitions[c].definitions&&this._getDefinitions(a.definitions[c],b+c+"/definitions/"))},_getExternalRefs:function(a){var b={},c=function(a){for(var c in a)a.hasOwnProperty(c)&&(b[c]=!0)};a.$ref&&"object"!=typeof a.$ref&&"#"!==a.$ref.substr(0,1)&&!this.refs[a.$ref]&&(b[a.$ref]=!0);for(var d in a)if(a.hasOwnProperty(d))if(a[d]&&"object"==typeof a[d]&&Array.isArray(a[d]))for(var e=0;e<a[d].length;e++)"object"==typeof a[d][e]&&c(this._getExternalRefs(a[d][e]));else a[d]&&"object"==typeof a[d]&&c(this._getExternalRefs(a[d]));return b},_loadExternalRefs:function(a,b){var c=this,e=this._getExternalRefs(a),f=0,g=0,h=!1;d(e,function(a){if(!c.refs[a]){if(!c.options.ajax)throw"Must set ajax option to true to load external ref "+a;c.refs[a]="loading",g++;var d=new XMLHttpRequest;d.open("GET",a,!0),d.onreadystatechange=function(){if(4==d.readyState){if(200!==d.status)throw window.console.log(d),"Failed to fetch ref via ajax- "+a;var e;try{e=JSON.parse(d.responseText)}catch(b){throw window.console.log(b),"Failed to parse external ref "+a}if(!e||"object"!=typeof e)throw"External ref does not contain a valid schema - "+a;c.refs[a]=e,c._loadExternalRefs(e,function(){f++,f>=g&&!h&&(h=!0,b())})}},d.send()}}),g||b()},expandRefs:function(a){for(a=c({},a);a.$ref;){var b=a.$ref;delete a.$ref,this.refs[b]||(b=decodeURIComponent(b)),a=this.extendSchemas(a,this.refs[b])}return a},expandSchema:function(a){var b,e=this,f=c({},a);if("object"==typeof a.type&&(Array.isArray(a.type)?d(a.type,function(b,c){"object"==typeof c&&(a.type[b]=e.expandSchema(c))}):a.type=e.expandSchema(a.type)),"object"==typeof a.disallow&&(Array.isArray(a.disallow)?d(a.disallow,function(b,c){"object"==typeof c&&(a.disallow[b]=e.expandSchema(c))}):a.disallow=e.expandSchema(a.disallow)),a.anyOf&&d(a.anyOf,function(b,c){a.anyOf[b]=e.expandSchema(c)}),a.dependencies&&d(a.dependencies,function(b,c){"object"!=typeof c||Array.isArray(c)||(a.dependencies[b]=e.expandSchema(c))}),a.not&&(a.not=this.expandSchema(a.not)),a.allOf){for(b=0;b<a.allOf.length;b++)f=this.extendSchemas(f,this.expandSchema(a.allOf[b]));delete f.allOf}if(a.extends){if(Array.isArray(a.extends))for(b=0;b<a.extends.length;b++)f=this.extendSchemas(f,this.expandSchema(a.extends[b]));else f=this.extendSchemas(f,this.expandSchema(a.extends));delete f.extends}if(a.oneOf){var g=c({},f);for(delete g.oneOf,b=0;b<a.oneOf.length;b++)f.oneOf[b]=this.extendSchemas(this.expandSchema(a.oneOf[b]),g)}return this.expandRefs(f)},extendSchemas:function(a,b){a=c({},a),b=c({},b);var e=this,f={};return d(a,function(a,c){"undefined"!=typeof b[a]?"required"!==a&&"defaultProperties"!==a||"object"!=typeof c||!Array.isArray(c)?"type"!==a||"string"!=typeof c&&!Array.isArray(c)?"object"==typeof c&&Array.isArray(c)?f[a]=c.filter(function(c){return b[a].indexOf(c)!==-1}):"object"==typeof c&&null!==c?f[a]=e.extendSchemas(c,b[a]):f[a]=c:("string"==typeof c&&(c=[c]),"string"==typeof b.type&&(b.type=[b.type]),b.type&&b.type.length?f.type=c.filter(function(a){return b.type.indexOf(a)!==-1}):f.type=c,1===f.type.length&&"string"==typeof f.type[0]?f.type=f.type[0]:0===f.type.length&&delete f.type):f[a]=c.concat(b[a]).reduce(function(a,b){return a.indexOf(b)<0&&a.push(b),a},[]):f[a]=c}),d(b,function(b,c){"undefined"==typeof a[b]&&(f[b]=c)}),f}},f.defaults={themes:{},templates:{},iconlibs:{},editors:{},languages:{},resolvers:[],custom_validators:[]},f.Validator=a.extend({init:function(a,b,c){this.jsoneditor=a,this.schema=b||this.jsoneditor.schema,this.options=c||{},this.translate=this.jsoneditor.translate||f.defaults.translate},validate:function(a){return this._validateSchema(this.schema,a)},_validateSchema:function(a,b,e){var g,h,i,j=this,k=[],l=JSON.stringify(b);if(e=e||"root",a=c({},this.jsoneditor.expandRefs(a)),a.required&&a.required===!0){if("undefined"==typeof b)return k.push({path:e,property:"required",message:this.translate("error_notset")}),k}else if("undefined"==typeof b){if(!this.jsoneditor.options.required_by_default)return k;k.push({path:e,property:"required",message:this.translate("error_notset")})}if(a.enum){for(g=!1,h=0;h<a.enum.length;h++)l===JSON.stringify(a.enum[h])&&(g=!0);g||k.push({path:e,property:"enum",message:this.translate("error_enum")})}if(a.extends)for(h=0;h<a.extends.length;h++)k=k.concat(this._validateSchema(a.extends[h],b,e));if(a.allOf)for(h=0;h<a.allOf.length;h++)k=k.concat(this._validateSchema(a.allOf[h],b,e));if(a.anyOf){for(g=!1,h=0;h<a.anyOf.length;h++)if(!this._validateSchema(a.anyOf[h],b,e).length){g=!0;break}g||k.push({path:e,property:"anyOf",message:this.translate("error_anyOf")})}if(a.oneOf){g=0;var m=[];for(h=0;h<a.oneOf.length;h++){var n=this._validateSchema(a.oneOf[h],b,e);for(n.length||g++,i=0;i<n.length;i++)n[i].path=e+".oneOf["+h+"]"+n[i].path.substr(e.length);m=m.concat(n)}1!==g&&(k.push({path:e,property:"oneOf",message:this.translate("error_oneOf",[g])}),k=k.concat(m))}if(a.not&&(this._validateSchema(a.not,b,e).length||k.push({path:e,property:"not",message:this.translate("error_not")})),a.type)if(Array.isArray(a.type)){for(g=!1,h=0;h<a.type.length;h++)if(this._checkType(a.type[h],b)){g=!0;break}g||k.push({path:e,property:"type",message:this.translate("error_type_union")})}else this._checkType(a.type,b)||k.push({path:e,property:"type",message:this.translate("error_type",[a.type])});if(a.disallow)if(Array.isArray(a.disallow)){for(g=!0,h=0;h<a.disallow.length;h++)if(this._checkType(a.disallow[h],b)){g=!1;break}g||k.push({path:e,property:"disallow",message:this.translate("error_disallow_union")})}else this._checkType(a.disallow,b)&&k.push({path:e,property:"disallow",message:this.translate("error_disallow",[a.disallow])});if("number"==typeof b){if(a.multipleOf||a.divisibleBy){var o=a.multipleOf||a.divisibleBy;g=b/o===Math.floor(b/o),window.math?g=window.math.mod(window.math.bignumber(b),window.math.bignumber(o)).equals(0):window.Decimal&&(g=new window.Decimal(b).mod(new window.Decimal(o)).equals(0)),g||k.push({path:e,property:a.multipleOf?"multipleOf":"divisibleBy",message:this.translate("error_multipleOf",[o])})}a.hasOwnProperty("maximum")&&(g=a.exclusiveMaximum?b<a.maximum:b<=a.maximum,window.math?g=window.math[a.exclusiveMaximum?"smaller":"smallerEq"](window.math.bignumber(b),window.math.bignumber(a.maximum)):window.Decimal&&(g=new window.Decimal(b)[a.exclusiveMaximum?"lt":"lte"](new window.Decimal(a.maximum))),g||k.push({path:e,property:"maximum",message:this.translate(a.exclusiveMaximum?"error_maximum_excl":"error_maximum_incl",[a.maximum])})),a.hasOwnProperty("minimum")&&(g=a.exclusiveMinimum?b>a.minimum:b>=a.minimum,window.math?g=window.math[a.exclusiveMinimum?"larger":"largerEq"](window.math.bignumber(b),window.math.bignumber(a.minimum)):window.Decimal&&(g=new window.Decimal(b)[a.exclusiveMinimum?"gt":"gte"](new window.Decimal(a.minimum))),g||k.push({path:e,property:"minimum",message:this.translate(a.exclusiveMinimum?"error_minimum_excl":"error_minimum_incl",[a.minimum])}))}else if("string"==typeof b)a.maxLength&&(b+"").length>a.maxLength&&k.push({path:e,property:"maxLength",message:this.translate("error_maxLength",[a.maxLength])}),a.minLength&&(b+"").length<a.minLength&&k.push({path:e,property:"minLength",message:this.translate(1===a.minLength?"error_notempty":"error_minLength",[a.minLength])}),a.pattern&&(new RegExp(a.pattern).test(b)||k.push({path:e,property:"pattern",message:this.translate("error_pattern",[a.pattern])}));else if("object"==typeof b&&null!==b&&Array.isArray(b)){if(a.items)if(Array.isArray(a.items))for(h=0;h<b.length;h++)if(a.items[h])k=k.concat(this._validateSchema(a.items[h],b[h],e+"."+h));else{if(a.additionalItems===!0)break;if(!a.additionalItems){if(a.additionalItems===!1){k.push({path:e,property:"additionalItems",message:this.translate("error_additionalItems")});break}break}k=k.concat(this._validateSchema(a.additionalItems,b[h],e+"."+h))}else for(h=0;h<b.length;h++)k=k.concat(this._validateSchema(a.items,b[h],e+"."+h));if(a.maxItems&&b.length>a.maxItems&&k.push({path:e,property:"maxItems",message:this.translate("error_maxItems",[a.maxItems])}),a.minItems&&b.length<a.minItems&&k.push({path:e,property:"minItems",message:this.translate("error_minItems",[a.minItems])}),a.uniqueItems){var p={};for(h=0;h<b.length;h++){if(g=JSON.stringify(b[h]),p[g]){k.push({path:e,property:"uniqueItems",message:this.translate("error_uniqueItems")});break}p[g]=!0}}}else if("object"==typeof b&&null!==b){if(a.maxProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g>a.maxProperties&&k.push({path:e,property:"maxProperties",message:this.translate("error_maxProperties",[a.maxProperties])})}if(a.minProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g<a.minProperties&&k.push({path:e,property:"minProperties",message:this.translate("error_minProperties",[a.minProperties])})}if(a.required&&Array.isArray(a.required))for(h=0;h<a.required.length;h++)"undefined"==typeof b[a.required[h]]&&k.push({path:e,property:"required",message:this.translate("error_required",[a.required[h]])});var q={};if(a.properties)for(h in a.properties)a.properties.hasOwnProperty(h)&&(q[h]=!0,k=k.concat(this._validateSchema(a.properties[h],b[h],e+"."+h)));if(a.patternProperties)for(h in a.patternProperties)if(a.patternProperties.hasOwnProperty(h)){var r=new RegExp(h);for(i in b)b.hasOwnProperty(i)&&r.test(i)&&(q[i]=!0,k=k.concat(this._validateSchema(a.patternProperties[h],b[i],e+"."+i)))}if("undefined"!=typeof a.additionalProperties||!this.jsoneditor.options.no_additional_properties||a.oneOf||a.anyOf||(a.additionalProperties=!1),"undefined"!=typeof a.additionalProperties)for(h in b)if(b.hasOwnProperty(h)&&!q[h]){if(!a.additionalProperties){k.push({path:e,property:"additionalProperties",message:this.translate("error_additional_properties",[h])});break}if(a.additionalProperties===!0)break;k=k.concat(this._validateSchema(a.additionalProperties,b[h],e+"."+h))}if(a.dependencies)for(h in a.dependencies)if(a.dependencies.hasOwnProperty(h)&&"undefined"!=typeof b[h])if(Array.isArray(a.dependencies[h]))for(i=0;i<a.dependencies[h].length;i++)"undefined"==typeof b[a.dependencies[h][i]]&&k.push({path:e,property:"dependencies",message:this.translate("error_dependency",[a.dependencies[h][i]])});else k=k.concat(this._validateSchema(a.dependencies[h],b,e))}return d(f.defaults.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),this.options.custom_validators&&d(this.options.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),k},_checkType:function(a,b){return"string"==typeof a?"string"===a?"string"==typeof b:"number"===a?"number"==typeof b:"integer"===a?"number"==typeof b&&b===Math.floor(b):"boolean"===a?"boolean"==typeof b:"array"===a?Array.isArray(b):"object"===a?null!==b&&!Array.isArray(b)&&"object"==typeof b:"null"!==a||null===b:!this._validateSchema(a,b).length}}),f.AbstractEditor=a.extend({onChildEditorChange:function(a){this.onChange(!0)},notify:function(){this.jsoneditor.notifyWatchers(this.path)},change:function(){this.parent?this.parent.onChildEditorChange(this):this.jsoneditor.onChange()},onChange:function(a){this.notify(),this.watch_listener&&this.watch_listener(),a&&this.change()},register:function(){this.jsoneditor.registerEditor(this),this.onChange()},unregister:function(){this.jsoneditor&&this.jsoneditor.unregisterEditor(this)},getNumColumns:function(){return 12},init:function(a){this.jsoneditor=a.jsoneditor,this.theme=this.jsoneditor.theme,this.template_engine=this.jsoneditor.template,this.iconlib=this.jsoneditor.iconlib,this.translate=this.jsoneditor.translate||f.defaults.translate,this.original_schema=a.schema,this.schema=this.jsoneditor.expandSchema(this.original_schema),this.options=c({},this.options||{},a.schema.options||{},a),a.path||this.schema.id||(this.schema.id="root"),this.path=a.path||"root",this.formname=a.formname||this.path.replace(/\.([^.]+)/g,"[$1]"),this.jsoneditor.options.form_name_root&&(this.formname=this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+"[")),this.key=this.path.split(".").pop(),this.parent=a.parent,this.link_watchers=[],a.container&&this.setContainer(a.container)},setContainer:function(a){this.container=a,this.schema.id&&this.container.setAttribute("data-schemaid",this.schema.id),this.schema.type&&"string"==typeof this.schema.type&&this.container.setAttribute("data-schematype",this.schema.type),this.container.setAttribute("data-schemapath",this.path)},preBuild:function(){},build:function(){},postBuild:function(){this.setupWatchListeners(),this.addLinks(),this.setValue(this.getDefault(),!0),this.updateHeaderText(),this.register(),this.onWatchedFieldChange()},setupWatchListeners:function(){var a=this;if(this.watched={},this.schema.vars&&(this.schema.watch=this.schema.vars),this.watched_values={},this.watch_listener=function(){a.refreshWatchedFieldValues()&&a.onWatchedFieldChange()},this.register(),this.schema.hasOwnProperty("watch")){var b,c,d,e,f;for(var g in this.schema.watch)if(this.schema.watch.hasOwnProperty(g)){if(b=this.schema.watch[g],Array.isArray(b)){if(b.length<2)continue;c=[b[0]].concat(b[1].split("."))}else c=b.split("."),a.theme.closest(a.container,'[data-schemaid="'+c[0]+'"]')||c.unshift("#");if(d=c.shift(),"#"===d&&(d=a.jsoneditor.schema.id||"root"),e=a.theme.closest(a.container,'[data-schemaid="'+d+'"]'),!e)throw"Could not find ancestor node with id "+d;f=e.getAttribute("data-schemapath")+"."+c.join("."),a.jsoneditor.watch(f,a.watch_listener),a.watched[g]=f}}this.schema.headerTemplate&&(this.header_template=this.jsoneditor.compileTemplate(this.schema.headerTemplate,this.template_engine))},addLinks:function(){if(!this.no_link_holder&&(this.link_holder=this.theme.getLinksHolder(),this.container.appendChild(this.link_holder),this.schema.links))for(var a=0;a<this.schema.links.length;a++)this.addLink(this.getLink(this.schema.links[a]))},getButton:function(a,b,c){var d="json-editor-btn-"+b;b=this.iconlib?this.iconlib.getIcon(b):null,!b&&c&&(a=c,c=null);var e=this.theme.getButton(a,b,c);return e.className+=" "+d+" ",e},setButtonText:function(a,b,c,d){return c=this.iconlib?this.iconlib.getIcon(c):null,!c&&d&&(b=d,d=null),this.theme.setButtonText(a,b,c,d)},addLink:function(a){this.link_holder&&this.link_holder.appendChild(a)},getLink:function(a){var b,c,d=a.mediaType||"application/javascript",e=d.split("/")[0],f=this.jsoneditor.compileTemplate(a.href,this.template_engine),g=null;if(a.download&&(g=a.download),g&&g!==!0&&(g=this.jsoneditor.compileTemplate(g,this.template_engine)),"image"===e){b=this.theme.getBlockLinkHolder(),c=document.createElement("a"),c.setAttribute("target","_blank");var h=document.createElement("img");this.theme.createImageLink(b,c,h),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.setAttribute("title",a.rel||d),h.setAttribute("src",d)})}else if(["audio","video"].indexOf(e)>=0){b=this.theme.getBlockLinkHolder(),c=this.theme.getBlockLink(),c.setAttribute("target","_blank");var i=document.createElement(e);i.setAttribute("controls","controls"),this.theme.createMediaLink(b,c,i),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.textContent=a.rel||d,i.setAttribute("src",d)})}else c=b=this.theme.getBlockLink(),b.setAttribute("target","_blank"),b.textContent=a.rel,this.link_watchers.push(function(c){var d=f(c);b.setAttribute("href",d),b.textContent=a.rel||d});return g&&c&&(g===!0?c.setAttribute("download",""):this.link_watchers.push(function(a){c.setAttribute("download",g(a))})),a.class&&(c.className=c.className+" "+a.class),b},refreshWatchedFieldValues:function(){if(this.watched_values){var a={},b=!1,c=this;if(this.watched){var d,e;for(var f in this.watched)this.watched.hasOwnProperty(f)&&(e=c.jsoneditor.getEditor(this.watched[f]),d=e?e.getValue():null,c.watched_values[f]!==d&&(b=!0),a[f]=d)}return a.self=this.getValue(),this.watched_values.self!==a.self&&(b=!0),this.watched_values=a,b}},getWatchedFieldValues:function(){return this.watched_values},updateHeaderText:function(){if(this.header)if(this.header.children.length){for(var a=0;a<this.header.childNodes.length;a++)if(3===this.header.childNodes[a].nodeType){this.header.childNodes[a].nodeValue=this.getHeaderText();break}}else this.header.textContent=this.getHeaderText()},getHeaderText:function(a){return this.header_text?this.header_text:a?this.schema.title:this.getTitle()},onWatchedFieldChange:function(){var a;if(this.header_template){a=c(this.getWatchedFieldValues(),{key:this.key,i:this.key,i0:1*this.key,i1:1*this.key+1,title:this.getTitle()});var b=this.header_template(a);b!==this.header_text&&(this.header_text=b,this.updateHeaderText(),this.notify())}if(this.link_watchers.length){a=this.getWatchedFieldValues();for(var d=0;d<this.link_watchers.length;d++)this.link_watchers[d](a)}},setValue:function(a){this.value=a},getValue:function(){return this.value},refreshValue:function(){},getChildEditors:function(){return!1},destroy:function(){var a=this;this.unregister(this),d(this.watched,function(b,c){a.jsoneditor.unwatch(c,a.watch_listener)}),this.watched=null,this.watched_values=null,this.watch_listener=null,this.header_text=null,this.header_template=null,this.value=null,this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container),this.container=null,this.jsoneditor=null,this.schema=null,this.path=null,this.key=null,this.parent=null},getDefault:function(){if(this.schema.default)return this.schema.default;if(this.schema.enum)return this.schema.enum[0];var a=this.schema.type||this.schema.oneOf;if(a&&Array.isArray(a)&&(a=a[0]),a&&"object"==typeof a&&(a=a.type),a&&Array.isArray(a)&&(a=a[0]),"string"==typeof a){if("number"===a)return 0;if("boolean"===a)return!1;if("integer"===a)return 0;if("string"===a)return"";if("object"===a)return{};if("array"===a)return[]}return null},getTitle:function(){return this.schema.title||this.key},enable:function(){this.disabled=!1},disable:function(){this.disabled=!0},isEnabled:function(){return!this.disabled},isRequired:function(){return"boolean"==typeof this.schema.required?this.schema.required:this.parent&&this.parent.schema&&Array.isArray(this.parent.schema.required)?this.parent.schema.required.indexOf(this.key)>-1:!!this.jsoneditor.options.required_by_default},getDisplayText:function(a){var b=[],c={};d(a,function(a,b){b.title&&(c[b.title]=c[b.title]||0,c[b.title]++),b.description&&(c[b.description]=c[b.description]||0,c[b.description]++),b.format&&(c[b.format]=c[b.format]||0,c[b.format]++),b.type&&(c[b.type]=c[b.type]||0,c[b.type]++)}),d(a,function(a,d){var e;e="string"==typeof d?d:d.title&&c[d.title]<=1?d.title:d.format&&c[d.format]<=1?d.format:d.type&&c[d.type]<=1?d.type:d.description&&c[d.description]<=1?d.description:d.title?d.title:d.format?d.format:d.type?d.type:d.description?d.description:JSON.stringify(d).length<50?JSON.stringify(d):"type",b.push(e)});var e={};return d(b,function(a,d){e[d]=e[d]||0,e[d]++,c[d]>1&&(b[a]=d+" "+e[d])}),b},getOption:function(a){try{throw"getOption is deprecated"}catch(a){window.console.error(a)}return this.options[a]},showValidationErrors:function(a){}}),f.defaults.editors.null=f.AbstractEditor.extend({getValue:function(){return null},setValue:function(){this.onChange()},getNumColumns:function(){return 2}}),f.defaults.editors.string=f.AbstractEditor.extend({register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},setValue:function(a,b,c){if((!this.template||c)&&(null===a||"undefined"==typeof a?a="":"object"==typeof a?a=JSON.stringify(a):"string"!=typeof a&&(a=""+a),a!==this.serialized)){var d=this.sanitize(a);if(this.input.value!==d){this.input.value=d,this.sceditor_instance?this.sceditor_instance.val(d):this.epiceditor?this.epiceditor.importFile(null,d):this.ace_editor&&this.ace_editor.setValue(d);var e=c||this.getValue()!==a;this.refreshValue(),b?this.is_dirty=!1:"change"===this.jsoneditor.options.show_errors&&(this.is_dirty=!0),this.adjust_height&&this.adjust_height(this.input),this.onChange(e)}}},getNumColumns:function(){var a,b=Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);return a="textarea"===this.input_type?6:["text","email"].indexOf(this.input_type)>=0?4:2,Math.min(12,Math.max(b,a))},build:function(){var a=this;if(this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.format=this.schema.format,!this.format&&this.schema.media&&this.schema.media.type&&(this.format=this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,"")),!this.format&&this.options.default_format&&(this.format=this.options.default_format),this.options.format&&(this.format=this.options.format),this.format)if("textarea"===this.format)this.input_type="textarea",this.input=this.theme.getTextareaInput();else if("range"===this.format){this.input_type="range";var b=this.schema.minimum||0,c=this.schema.maximum||Math.max(100,b+1),d=1;this.schema.multipleOf&&(b%this.schema.multipleOf&&(b=Math.ceil(b/this.schema.multipleOf)*this.schema.multipleOf),c%this.schema.multipleOf&&(c=Math.floor(c/this.schema.multipleOf)*this.schema.multipleOf),d=this.schema.multipleOf),this.input=this.theme.getRangeInput(b,c,d)}else["actionscript","batchfile","bbcode","c","c++","cpp","coffee","csharp","css","dart","django","ejs","erlang","golang","groovy","handlebars","haskell","haxe","html","ini","jade","java","javascript","json","less","lisp","lua","makefile","markdown","matlab","mysql","objectivec","pascal","perl","pgsql","php","python","r","ruby","sass","scala","scss","smarty","sql","stylus","svg","twig","vbscript","xml","yaml"].indexOf(this.format)>=0?(this.input_type=this.format,this.source_code=!0,this.input=this.theme.getTextareaInput()):(this.input_type=this.format,this.input=this.theme.getFormInputField(this.input_type));else this.input_type="text",this.input=this.theme.getFormInputField(this.input_type);"undefined"!=typeof this.schema.maxLength&&this.input.setAttribute("maxlength",this.schema.maxLength),"undefined"!=typeof this.schema.pattern?this.input.setAttribute("pattern",this.schema.pattern):"undefined"!=typeof this.schema.minLength&&this.input.setAttribute("pattern",".{"+this.schema.minLength+",}"),this.options.compact?this.container.className+=" compact":this.options.input_width&&(this.input.style.width=this.options.input_width),(this.schema.readOnly||this.schema.readonly||this.schema.template)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),a.schema.template)return void(this.value=a.value);var c=this.value,d=a.sanitize(c);c!==d&&(this.value=d),a.is_dirty=!0,a.refreshValue(),a.onChange(!0)}),this.options.input_height&&(this.input.style.height=this.options.input_height),this.options.expand_height&&(this.adjust_height=function(a){if(a){var b,c=a.offsetHeight;if(a.offsetHeight<a.scrollHeight)for(b=0;a.offsetHeight<a.scrollHeight+3&&!(b>100);)b++,c++,a.style.height=c+"px";else{for(b=0;a.offsetHeight>=a.scrollHeight+3&&!(b>100);)b++,c--,a.style.height=c+"px";a.style.height=c+1+"px"}}},this.input.addEventListener("keyup",function(b){a.adjust_height(this)}),this.input.addEventListener("change",function(b){a.adjust_height(this)}),this.adjust_height()),this.format&&this.input.setAttribute("data-schemaformat",this.format),this.control=this.theme.getFormControl(this.label,this.input,this.description),this.container.appendChild(this.control),window.requestAnimationFrame(function(){a.input.parentNode&&a.afterInputReady(),a.adjust_height&&a.adjust_height(a.input)}),this.schema.template?(this.template=this.jsoneditor.compileTemplate(this.schema.template,this.template_engine),this.refreshValue()):this.refreshValue()},enable:function(){this.always_disabled||(this.input.disabled=!1),this._super()},disable:function(){this.input.disabled=!0,this._super()},afterInputReady:function(){var a,b=this;if(this.source_code)if(this.options.wysiwyg&&["html","bbcode"].indexOf(this.input_type)>=0&&window.jQuery&&window.jQuery.fn&&window.jQuery.fn.sceditor)a=c({},{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not change this please, revert.

Revert the change in openwisp_controller/config/static/config/js/lib/jsonschema-ui.js because it is a minified third-party library and modifying it causes an unwanted large diff.
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 16, 2026
@openwisp-companion

Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

@openwisp-companion

Copy link
Copy Markdown

Test Failures in CI

Hello @ApurveKaranwal,
(Analysis for commit 75245ea)

  1. Test Failure: The test_real_time_update_address_field in openwisp_controller.geo.tests.test_selenium.TestDeviceAdminGeoSelenium failed due to a TimeoutException. This indicates that the Selenium test was unable to find an alert element within the specified timeout.
  • Fix: Investigate the test environment and the application's behavior to understand why the alert is not appearing or is taking too long to appear. This might involve debugging the Selenium test itself or the application logic it's testing.
  1. Runtime Warnings: Several RuntimeWarning: DateTimeField ... received a naive datetime while time zone support is active. warnings were observed. While not directly causing the failure, these indicate potential issues with timezone handling in the application.
  • Fix: Ensure that all DateTimeField instances are consistently handled with timezone information. This usually involves using timezone-aware datetime objects when saving or retrieving data.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openwisp_controller/geo/tests/test_selenium.py`:
- Around line 98-100: The move_by_offset(30, 15) call in the ActionChains method
chain uses magic numbers without explanation, making the code unclear and
fragile. Replace the numeric literals 30 and 15 with named constants (for
example, OFFSET_X and OFFSET_Y) defined at the top of the test class or test
method, and add a concise comment explaining why this specific offset is
necessary for the map interaction (such as ensuring the click lands on a
specific map element or avoiding unintended interactions). This makes the test
more maintainable and helps future developers understand the intent.
- Around line 21-22: The patches to geocoding_views.geocode and
geocoding_views.reverse_geocode are applied at module-level (import time),
causing mocked behavior to leak into other tests and create order-dependent
failures. Move these MagicMock assignments from the module-level scope into the
appropriate test setup method (such as setUp in a test class) or use a context
manager like unittest.mock.patch to scope the mocks to individual test
lifecycles, ensuring the originals are properly restored after each test
completes.
- Around line 103-106: Replace the fixed sleep(0.05) call with an explicit wait
condition that checks for the address field update before proceeding. Use
WebDriverWait to wait until the address_input element's value attribute contains
"Lazio 00185, ITA" (the new_address value), then remove the sleep statement
entirely. This will make the test deterministic and eliminate the
timing-sensitive failure mode in CI environments. The wait should target the
element retrieved by By.ID with value "id_address" and poll until the condition
is satisfied before the assertion at Line 106.

In `@openwisp_controller/pki/base/models.py`:
- Around line 18-22: Replace `datetime.now()` with `timezone.now()` when
calculating the `start` variable to ensure Django's configured timezone is
consistently used throughout the calculation. The `timezone.now()` function
automatically respects Django's `USE_TZ` setting and will return either a
timezone-aware or naive datetime as appropriate, eliminating the mismatch
between how the initial datetime is obtained and how it is later made aware,
which prevents date shifting when the server timezone differs from the Django
configured timezone.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c07eeb77-faf6-4ced-b847-8226a8318f96

📥 Commits

Reviewing files that changed from the base of the PR and between 04c2b46 and 6b77f61.

📒 Files selected for processing (4)
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/pki/base/models.py
  • openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
  • tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}

📄 CodeRabbit inference engine (Custom checks)

**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously

Files:

  • tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py
  • openwisp_controller/pki/base/models.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}

📄 CodeRabbit inference engine (Custom checks)

Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries

Files:

  • tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py
  • openwisp_controller/pki/base/models.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py
  • openwisp_controller/pki/base/models.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
**/*.{py,html}

📄 CodeRabbit inference engine (Custom checks)

For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework

Files:

  • tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py
  • openwisp_controller/pki/base/models.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it

Files:

  • tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py
  • openwisp_controller/pki/base/models.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
🧠 Learnings (6)
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.

Applied to files:

  • tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py
  • openwisp_controller/pki/base/models.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.

Applied to files:

  • tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py
  • openwisp_controller/geo/tests/test_selenium.py
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.

Applied to files:

  • openwisp_controller/pki/base/models.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.

Applied to files:

  • openwisp_controller/pki/base/models.py
  • openwisp_controller/geo/tests/test_selenium.py
  • openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.

Applied to files:

  • openwisp_controller/geo/tests/test_selenium.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.

Applied to files:

  • openwisp_controller/geo/tests/test_selenium.py
🔇 Additional comments (3)
openwisp_controller/pki/migrations/0013_alter_ca_validity_start_alter_cert_validity_start.py (1)

12-30: LGTM!

tests/openwisp2/sample_pki/migrations/0005_alter_ca_validity_start_alter_cert_validity_start.py (1)

14-32: LGTM!

openwisp_controller/geo/tests/test_selenium.py (1)

15-20: LGTM!

Also applies to: 81-102, 107-113, 157-177

Comment on lines +21 to +22
geocoding_views.geocode = MagicMock(return_value=MockLocation())
geocoding_views.reverse_geocode = MagicMock(return_value=MockLocation())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid module-level global monkeypatching of geocoding_views in tests.

At Line 21 and Line 22, patching geocoding_views.geocode / reverse_geocode at import time can leak mocked behavior into other tests in the same process and create order-dependent failures. Scope these mocks to the test/class lifecycle and restore originals.

Proposed scoped patching approach
+from unittest.mock import patch
+
 class TestDeviceAdminGeoSelenium(
@@
     inline_field_prefix = "devicelocation"
+
+    `@classmethod`
+    def setUpClass(cls):
+        super().setUpClass()
+        cls._geocode_patcher = patch.object(
+            geocoding_views, "geocode", return_value=MockLocation()
+        )
+        cls._reverse_geocode_patcher = patch.object(
+            geocoding_views, "reverse_geocode", return_value=MockLocation()
+        )
+        cls._geocode_patcher.start()
+        cls._reverse_geocode_patcher.start()
+
+    `@classmethod`
+    def tearDownClass(cls):
+        cls._reverse_geocode_patcher.stop()
+        cls._geocode_patcher.stop()
+        super().tearDownClass()
@@
-geocoding_views.geocode = MagicMock(return_value=MockLocation())
-geocoding_views.reverse_geocode = MagicMock(return_value=MockLocation())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_controller/geo/tests/test_selenium.py` around lines 21 - 22, The
patches to geocoding_views.geocode and geocoding_views.reverse_geocode are
applied at module-level (import time), causing mocked behavior to leak into
other tests and create order-dependent failures. Move these MagicMock
assignments from the module-level scope into the appropriate test setup method
(such as setUp in a test class) or use a context manager like
unittest.mock.patch to scope the mocks to individual test lifecycles, ensuring
the originals are properly restored after each test completes.

Comment on lines +98 to +100
ActionChains(self.web_driver).move_to_element(elem).move_by_offset(
30, 15
).click().perform()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Document the map-click offset rationale or replace magic numbers with named constants.

At Line 98-Line 100, move_by_offset(30, 15) is non-obvious and fragile without context. Add a brief “why” comment and/or constants so future maintainers know why this offset is acceptable.

As per coding guidelines, cryptic code must include a concise explanation of why the complexity is needed and acceptable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_controller/geo/tests/test_selenium.py` around lines 98 - 100, The
move_by_offset(30, 15) call in the ActionChains method chain uses magic numbers
without explanation, making the code unclear and fragile. Replace the numeric
literals 30 and 15 with named constants (for example, OFFSET_X and OFFSET_Y)
defined at the top of the test class or test method, and add a concise comment
explaining why this specific offset is necessary for the map interaction (such
as ensuring the click lands on a specific map element or avoiding unintended
interactions). This makes the test more maintainable and helps future developers
understand the intent.

Source: Coding guidelines

Comment on lines +103 to +106
sleep(0.05)
new_address = "Lazio 00185, ITA"
address_input = self.find_element(by=By.ID, value="id_address")
self.assertIn(new_address, address_input.get_attribute("value"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace fixed sleep with an explicit wait on address-field update.

At Line 103, sleep(0.05) is timing-sensitive and can still fail under CI jitter. Wait directly for the address value condition before asserting at Line 106.

Proposed deterministic wait
-        sleep(0.05)
         new_address = "Lazio 00185, ITA"
-        address_input = self.find_element(by=By.ID, value="id_address")
-        self.assertIn(new_address, address_input.get_attribute("value"))
+        WebDriverWait(self.web_driver, 10).until(
+            lambda d: new_address in d.find_element(By.ID, "id_address").get_attribute("value")
+        )
+        address_input = self.find_element(by=By.ID, value="id_address")
+        self.assertIn(new_address, address_input.get_attribute("value"))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_controller/geo/tests/test_selenium.py` around lines 103 - 106,
Replace the fixed sleep(0.05) call with an explicit wait condition that checks
for the address field update before proceeding. Use WebDriverWait to wait until
the address_input element's value attribute contains "Lazio 00185, ITA" (the
new_address value), then remove the sleep statement entirely. This will make the
test deterministic and eliminate the timing-sensitive failure mode in CI
environments. The wait should target the element retrieved by By.ID with value
"id_address" and poll until the condition is satisfied before the assertion at
Line 106.

Comment on lines +18 to +22
start = datetime.now() - timedelta(days=1)
start = start.replace(hour=0, minute=0, second=0, microsecond=0)
if settings.USE_TZ:
return timezone.make_aware(start)
return start

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Build validity_start from Django timezone, not host-local naive time.

Current logic can shift the date when server timezone and Django configured timezone differ, because datetime.now() and timezone.make_aware() may use different timezone bases. Use timezone.now() as the source and normalize from there.

Proposed fix
 def default_validity_start():
-    from datetime import datetime, timedelta
+    from datetime import timedelta
     from django.conf import settings
     from django.utils import timezone
-    start = datetime.now() - timedelta(days=1)
-    start = start.replace(hour=0, minute=0, second=0, microsecond=0)
+    if settings.USE_TZ:
+        start = timezone.localtime(timezone.now()) - timedelta(days=1)
+    else:
+        start = timezone.now() - timedelta(days=1)
+    start = start.replace(hour=0, minute=0, second=0, microsecond=0)
     if settings.USE_TZ:
-        return timezone.make_aware(start)
+        return timezone.make_aware(start) if timezone.is_naive(start) else start
     return start
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openwisp_controller/pki/base/models.py` around lines 18 - 22, Replace
`datetime.now()` with `timezone.now()` when calculating the `start` variable to
ensure Django's configured timezone is consistently used throughout the
calculation. The `timezone.now()` function automatically respects Django's
`USE_TZ` setting and will return either a timezone-aware or naive datetime as
appropriate, eliminating the mismatch between how the initial datetime is
obtained and how it is later made aware, which prevents date shifting when the
server timezone differs from the Django configured timezone.

@nemesifier

Copy link
Copy Markdown
Member

You started changing things that don't have anything to do with spelling mistakes. I am sorry but we don't have time to deal with this kind of work right now in the @openwisp project.

@nemesifier nemesifier closed this Jun 16, 2026
@openwisp openwisp locked as spam and limited conversation to collaborators Jun 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[docs] Fix various spelling and grammatical mistakes in comments and templates

3 participants