From d76ede17d30548d311b89c33ae77377909f3b2dc Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sat, 23 Sep 2023 21:33:47 +0200 Subject: [PATCH 001/303] Add data properties for device interface table Preparatory work for factoring row styling decisions out of Python code. --- netbox/dcim/tables/devices.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 34dbcbf30..fc8a3af73 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -69,6 +69,36 @@ def get_interface_state_attribute(record): return "disabled" +def get_interface_virtual_attribute(record): + """ + Get interface virtual state as string to attach to DOM element. + """ + if record.is_virtual: + return "true" + else: + return "false" + + +def get_interface_mark_connected_attribute(record): + """ + Get interface enabled state as string to attach to DOM element. + """ + if record.mark_connected: + return "true" + else: + return "false" + + +def get_interface_cable_status_attribute(record): + """ + Get interface enabled state as string to attach to DOM element. + """ + if record.cable: + return record.cable.status + else: + return "" + + # # Device roles # @@ -673,6 +703,9 @@ class DeviceInterfaceTable(InterfaceTable): 'class': get_interface_row_class, 'data-name': lambda record: record.name, 'data-enabled': get_interface_state_attribute, + 'data-virtual': get_interface_virtual_attribute, + 'data-mark-connected': get_interface_mark_connected_attribute, + 'data-cable-status': get_interface_cable_status_attribute, 'data-type': lambda record: record.type, } From 41e1f24cf7c1c7431088eda7e9feebd323e9711b Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sat, 23 Sep 2023 21:43:32 +0200 Subject: [PATCH 002/303] Add --nbx-color-* variables for theme colors Preparatory work for moving row styling to CSS --- netbox/project-static/dist/netbox-dark.css | Bin 375591 -> 376198 bytes netbox/project-static/dist/netbox-light.css | Bin 232798 -> 233371 bytes netbox/project-static/dist/netbox-print.css | Bin 727883 -> 728556 bytes netbox/project-static/styles/netbox.scss | 7 +++++++ 4 files changed, 7 insertions(+) diff --git a/netbox/project-static/dist/netbox-dark.css b/netbox/project-static/dist/netbox-dark.css index 2d7142bc6b6da7f32935de02c63becb00dc33c22..f9d1b619bcf5d7ce3693baa5ed80987964b0a350 100644 GIT binary patch delta 548 zcmZvYy-LJD6opw$thBI6Cx}*p192sP+W0DWGIz3r<0MS7tSK}W!56SdYbRR@!akMQ z+1NOf1#w)<;X7x}x##6Gc>D?;`bodlZ};At|GFIZ-o4$Uyp*LTl@>Rk+l#~F7$N0& zNreH$j)A^5B49M4%n?yJ$1(SBdDDoA!ghl5B;<+Xs^+?q)(2UN%&k-j zWwS6&BO0w;-FgqAky)}?wbb?w!#L7uGIg?Al(Jk{wt0S`5tTvz4B8+_OESju(=+GC qyaj8DabxR3Lpv^ZKqch%j4%ux?_wqz9Kt*AWVmAf&T6&vtNm}BAh&k_ delta 25 hcmZqsEw=odSVIeA3sVbo3(FSPjVrectYIw^1OSbd3HJa1 diff --git a/netbox/project-static/dist/netbox-light.css b/netbox/project-static/dist/netbox-light.css index ffdd83285e235e4f723f8f46937a2c0ab5033570..66829e602dd097a0d7a9fff33bb7b047ba68c82c 100644 GIT binary patch delta 589 zcmZvZO-{ow5JpwHMDz?utRp#f>@?*dtex>VwQB6hNmVKqp;B+a4i>;2kl1k)&VsG* zXVLZZ%s1cj$HVOLX?Ay*98O-RFZ*HXo$GJ2%+@yJYUlbq5;+S;Rx;Q#5)hhFzrle6L1{SJ+B+wlH~hQ=e>fR32$QJ6sH1ga`^2RjjG phy?NDe3a055&u}onzA~TP#XcU4I!zw?MnBtzf$V$c|UzW{RBeQzuEu* delta 26 icmbO|pYPr*zJ?aY7N#xC3mc~&sAp!|{-l|CJ2wEAwhHwC diff --git a/netbox/project-static/dist/netbox-print.css b/netbox/project-static/dist/netbox-print.css index b492e4d1dbd0bfe6538e68d291f6d78972bb52ea..5368b2956d88f1f8c109c0dd4b7fccd8706310dd 100644 GIT binary patch delta 576 zcmZY6y-ve06a`=vXrWy}VnyoONKW$)&%)r?*Qtf&ASYGG6b4Uhh@k^g-+%?7GB7do zDhzC0v4No`qxH$x=N^A-W}k1fTRqTwdS4&tp&sc&eWb@Pll6A>csITrl2VlF{_CWZ z`HlB;@G2msq*%4*XVaJ@Q9>^~Z-*!*wa zZxIjggX5hhuDP`1X&eNpyQczbp&zzX*lc2q(FA<=QVT+qjkSIQg#wU<{H*JhS_2k_ z=7?mO+Z$=Iy@V*i|LQIjw*QA|9_8--xxmD3g^=4=v*5K2zIskar?2bnF7M2#)7Pc1l7LFFqEnM@yZ@2%&wN@Db|HTdO diff --git a/netbox/project-static/styles/netbox.scss b/netbox/project-static/styles/netbox.scss index 94fddc32c..f7d3d7069 100644 --- a/netbox/project-static/styles/netbox.scss +++ b/netbox/project-static/styles/netbox.scss @@ -835,6 +835,13 @@ table tbody { } } +// Expose theme colors as variables. (Useful for dynamic styling of choices etc.) +:root { + @each $color, $value in $theme-colors { + --nbx-color-#{$color}: #{$value}; + } +} + // Style objects with statuses/roles within a table. As of implementation, used for IP addresses // assigned to interfaces. table .table-badge-group { From d44f67aea5c37d43075ef81a94ccaeff964c02a6 Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sat, 23 Sep 2023 23:01:08 +0200 Subject: [PATCH 003/303] Add 15% alpha variants of --nbx-color Preparatory work for factoring row styling out of Python --- netbox/project-static/dist/netbox-dark.css | Bin 376198 -> 377206 bytes netbox/project-static/dist/netbox-light.css | Bin 233371 -> 234355 bytes netbox/project-static/dist/netbox-print.css | Bin 728556 -> 729540 bytes netbox/project-static/styles/netbox.scss | 2 ++ 4 files changed, 2 insertions(+) diff --git a/netbox/project-static/dist/netbox-dark.css b/netbox/project-static/dist/netbox-dark.css index f9d1b619bcf5d7ce3693baa5ed80987964b0a350..3032fe467ea47d59c64340686dd42ea368d0ac99 100644 GIT binary patch delta 850 zcmZvaKTE?v7{>9>iG!dn>L8L@qgER6u1Q;UaC33>3tZA`4928fTWbeFqzFzO9J0AM zHE|IHCj~!%lV8Bq(M1p!--{_+Xd&DXe)qi3`#g8`JNJD#f6*D~jCRI4ZAK1%B@tW zXEQF71u%`{LkH#9z8RwRtn1iE4H+chjSx^H1@IAr=iqV@c03l`|BkmIQkG4($)u~p zB64w=L=-eALQlh@poVsRTlU4UZA82l8G)f_K2lav!%#RZ{S z#tAnTq16m$6=^OR62f8xars>2C6l6KToS?Je%ob1wwdettrR&}!C?f!+MF>@0x=$} z-;ZFI2BWUb5!fKEA8+@Yx)z+(rfI#wXL2Vvn^XDWk|BjYM@T&|U24f8g53WnV&2s> n&pgvC-}SlHI&vCpN6A*J)xrP5`c1dH5WMb^6{4^QK*t_n|u z8aaY_2bKz2qU^D%#W=^(bgR}b^D6n{7@TG4Cvm$#^Q##s<-f*Y+r5GB%U;hA;2!q6 zSeo^s|IZk9N@3hd42phoMx|aWu(cFE*+Ze?Fb)iodnCAzX7&oOkwRs$E#g2b;_~0K zunJ`KD21{eg~3VR?02!1yA3M)g~DkHI~zF7SA+9#X0KVb8WY=>2$fpVKpGH3EymC> z_a^DwpquEo=14|SGMobc&-zOSI1W5Hqm oJtY3wapKmQ<0ny~oZ(5>mj{;J>2%(I*B-AAVeR21gP%)(0kTillK=n! delta 125 zcmeyojBoaQzJ?aY7N#xC7aFHCHZdzq4{c(WnZB@zS!(*rCT7{`X3aouV>7e#^fS#s zF`*V95!C_|Th;=U{oKMVIo+z2S#oj^qtNt&t;}lE#oB;sxs TJ2Ut6_zq^Z?YG;RXK(`mA51Je diff --git a/netbox/project-static/dist/netbox-print.css b/netbox/project-static/dist/netbox-print.css index 5368b2956d88f1f8c109c0dd4b7fccd8706310dd..3c58d79c006a25c5beea279a242142bc7c8fcde4 100644 GIT binary patch delta 807 zcmZXSzfK!L5XJ>|g1u=-;TE5LC>V5yylbETfKZeoU3dYywS7KLIqs~L!Er~iU67EV zxRw{7L!zJri6_Vd&__zUN5#xy!xE&^>a_apH{X0Se{Y<-Tjy9V$VHixdHF$pluNQ8 zi&tbtt&T)^z{S+%)Z4N|(Bn0aIu>ONj!wg|HmSFsepbXy%Ze28$fe(Z7b|I75j)*c zWa;_t_@r3MA-k){S|yB!i6Pl(Iu@gNH0v|TK#>i5!bKd$fk86QgTh@X+@66_k@f0; z$AK?Qj)9N|SgSkZE)=QRQ5fuxO{@V3Yqao~gEwHXJmBJ|!FkZG*8w%~s5}0HUG$K1UiVa=^&P8S>D8B8waqpMgPohM)ll^e)Z&~(mIDCJvJ)SS;OF4L#R;-26_18yo GX+Htfc;iz5 delta 148 zcmX?dSm(`dorV_17N!>F7M2#)7Pc1l7LFFqEnIRxrsw_O;+!7wgG+q+;vZZx)8GH# zlA3P*lS_7b%TFNp{7)|F=~BOdVhJE(#V?@PmtR0x$KPC%(>p-K+224h$v;3M Date: Sat, 23 Sep 2023 23:07:16 +0200 Subject: [PATCH 004/303] Move DeviceInterfaceTable coloring logic into CSS Preparatory work for simplifying toggle button code for cable status. --- netbox/dcim/tables/devices.py | 11 ++--------- netbox/templates/dcim/device/interfaces.html | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index fc8a3af73..063e05215 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -6,6 +6,7 @@ from dcim import models from netbox.tables import NetBoxTable, columns from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin from .template_code import * +from dcim.choices import LinkStatusChoices __all__ = ( 'BaseInterfaceTable', @@ -51,14 +52,6 @@ def get_cabletermination_row_class(record): return '' -def get_interface_row_class(record): - if not record.enabled: - return 'danger' - elif record.is_virtual: - return 'primary' - return get_cabletermination_row_class(record) - - def get_interface_state_attribute(record): """ Get interface enabled state as string to attach to DOM element. @@ -700,7 +693,6 @@ class DeviceInterfaceTable(InterfaceTable): 'cable', 'connection', ) row_attrs = { - 'class': get_interface_row_class, 'data-name': lambda record: record.name, 'data-enabled': get_interface_state_attribute, 'data-virtual': get_interface_virtual_attribute, @@ -708,6 +700,7 @@ class DeviceInterfaceTable(InterfaceTable): 'data-cable-status': get_interface_cable_status_attribute, 'data-type': lambda record: record.type, } + cable_status_styles = [(slug, color) for slug, _, color in LinkStatusChoices.CHOICES] class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable): diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index 8b3fe3097..54682439c 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -30,3 +30,23 @@ {% endif %} {% endblock bulk_extra_controls %} + +{% block head %} + {{ block.super }} + +{% endblock %} From 83e2c45e74e2a28df8c9b3b42a89fd61788f631d Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sat, 23 Sep 2023 23:45:08 +0200 Subject: [PATCH 005/303] Simplify mark connected/installed implementation Fixes: #13712 and #13806. --- netbox/project-static/dist/netbox.js | Bin 530368 -> 529923 bytes netbox/project-static/dist/netbox.js.map | Bin 450659 -> 450364 bytes .../src/buttons/connectionToggle.ts | 36 +++++------------- netbox/templates/dcim/device/interfaces.html | 9 ++++- .../dcim/inc/cable_toggle_buttons.html | 15 +++----- 5 files changed, 24 insertions(+), 36 deletions(-) diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 84bfecae34920e732ecf52670208390ce6712040..d457ae229f71cfb090301054275a1d36b6ddfb16 100644 GIT binary patch delta 15474 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 delta 15773 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 Iygcgv0oR~A9smFU diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 7f2400ed2610973ed909703e534ec9b73c19c8ae..51042c43167bdeace2fa7ed21428254800856a7e 100644 GIT binary patch delta 181 zcmaESKzh$U>4p}@7N!>F7M3ln%-3B7Smnf> z&A~FhAVq;XiH?qmI+2d9S<@rmu^KWv7dTI!{EpRJ(%n(V)6vlrBA@T%=ve5a(yTx>4GnRCy4Gw&b6pU>e_xlwMGPT6}^PJJ@cbOc;8G1wiOI-5nwxLRVk4IvC6}h3si>{C2id->YA7Q-uZq diff --git a/netbox/project-static/src/buttons/connectionToggle.ts b/netbox/project-static/src/buttons/connectionToggle.ts index 74b32dc3a..ed119f738 100644 --- a/netbox/project-static/src/buttons/connectionToggle.ts +++ b/netbox/project-static/src/buttons/connectionToggle.ts @@ -7,10 +7,10 @@ import { isTruthy, apiPatch, hasError, getElements } from '../util'; * * @param element Connection Toggle Button Element */ -function toggleConnection(element: HTMLButtonElement): void { +function setConnectionStatus(element: HTMLButtonElement, status: string): void { + // Get the button's row to change its data-cable-status attribute + const row = element.parentElement?.parentElement as HTMLTableRowElement; const url = element.getAttribute('data-url'); - const connected = element.classList.contains('connected'); - const status = connected ? 'planned' : 'connected'; if (isTruthy(url)) { apiPatch(url, { status }).then(res => { @@ -19,34 +19,18 @@ function toggleConnection(element: HTMLButtonElement): void { createToast('danger', 'Error', res.error).show(); return; } else { - // Get the button's row to change its styles. - const row = element.parentElement?.parentElement as HTMLTableRowElement; - // Get the button's icon to change its CSS class. - const icon = element.querySelector('i.mdi, span.mdi') as HTMLSpanElement; - if (connected) { - row.classList.remove('success'); - row.classList.add('info'); - element.classList.remove('connected', 'btn-warning'); - element.classList.add('btn-info'); - element.title = 'Mark Installed'; - icon.classList.remove('mdi-lan-disconnect'); - icon.classList.add('mdi-lan-connect'); - } else { - row.classList.remove('info'); - row.classList.add('success'); - element.classList.remove('btn-success'); - element.classList.add('connected', 'btn-warning'); - element.title = 'Mark Installed'; - icon.classList.remove('mdi-lan-connect'); - icon.classList.add('mdi-lan-disconnect'); - } + // Update cable status in DOM + row.setAttribute('data-cable-status', status); } }); } } export function initConnectionToggle(): void { - for (const element of getElements('button.cable-toggle')) { - element.addEventListener('click', () => toggleConnection(element)); + for (const element of getElements('button.mark-planned')) { + element.addEventListener('click', () => setConnectionStatus(element, 'planned')); + } + for (const element of getElements('button.mark-installed')) { + element.addEventListener('click', () => setConnectionStatus(element, 'connected')); } } diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index 54682439c..c4aee9784 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -48,5 +48,12 @@ tr[data-enabled=disabled] { background-color: var(--nbx-color-danger-a15); } - + tr[data-cable-status=connected] button.mark-installed { + display: none; + } + tr:not([data-cable-status=connected]) button.mark-planned { + display: none; + } + + {% endblock %} diff --git a/netbox/templates/dcim/inc/cable_toggle_buttons.html b/netbox/templates/dcim/inc/cable_toggle_buttons.html index 4d8d995c4..1c5427337 100644 --- a/netbox/templates/dcim/inc/cable_toggle_buttons.html +++ b/netbox/templates/dcim/inc/cable_toggle_buttons.html @@ -1,12 +1,9 @@ {% load i18n %} {% if perms.dcim.change_cable %} - {% if cable.status == 'connected' %} - - {% else %} - - {% endif %} + + {% endif %} From c728d3c2e83cbfebfacf564ea8d3dae79dedd5ac Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sun, 24 Sep 2023 00:08:39 +0200 Subject: [PATCH 006/303] Fix formatting --- netbox/templates/dcim/device/interfaces.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index c4aee9784..8669789c7 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -35,9 +35,9 @@ {{ block.super }} + {% endblock %} From 6af3aad36262713c22647f7f9d2565e038ae0b02 Mon Sep 17 00:00:00 2001 From: Moritz Geist Date: Tue, 9 Jan 2024 13:51:09 +0100 Subject: [PATCH 007/303] Fixes #14722, Fixes #13922: Update the CableRender This commit updates the cable rendering logic to fix both issue #14722 and #13922. Before, objects, terminations and cables where drawn in the svg without context of each other. Now the following changes are applied: - Hosts and Terminations are where possible sorted alphabetically - Terminations and Cables are visually connected, and if necessary not in a vertical line - Terminations and Hosts are visually aligning - Cable Tooltips contain more information --- netbox/dcim/svg/cables.py | 363 ++++++++++++++++++-------------------- 1 file changed, 174 insertions(+), 189 deletions(-) diff --git a/netbox/dcim/svg/cables.py b/netbox/dcim/svg/cables.py index d7365161e..76d6dc68a 100644 --- a/netbox/dcim/svg/cables.py +++ b/netbox/dcim/svg/cables.py @@ -8,17 +8,16 @@ from django.conf import settings from dcim.constants import CABLE_TRACE_SVG_DEFAULT_WIDTH from utilities.utils import foreground_color - __all__ = ( 'CableTraceSVG', ) - OFFSET = 0.5 PADDING = 10 LINE_HEIGHT = 20 FANOUT_HEIGHT = 35 FANOUT_LEG_HEIGHT = 15 +CABLE_HEIGHT = 4 * LINE_HEIGHT + FANOUT_HEIGHT + FANOUT_LEG_HEIGHT class Node(Hyperlink): @@ -84,31 +83,38 @@ class Connector(Group): labels: Iterable of text labels """ - def __init__(self, start, url, color, labels=[], description=[], **extra): - super().__init__(class_='connector', **extra) + def __init__(self, start, url, color, wireless, labels=[], description=[], end=None, text_offset=0, **extra): + super().__init__(class_="connector", **extra) self.start = start self.height = PADDING * 2 + LINE_HEIGHT * len(labels) + PADDING * 2 - self.end = (start[0], start[1] + self.height) + # Allow to specify end-position or auto-calculate + self.end = end if end else (start[0], start[1] + self.height) self.color = color or '000000' - # Draw a "shadow" line to give the cable a border - cable_shadow = Line(start=self.start, end=self.end, class_='cable-shadow') - self.add(cable_shadow) + if wireless: + # Draw the cable + cable = Line(start=self.start, end=self.end, class_="wireless-link") + self.add(cable) + else: + # Draw a "shadow" line to give the cable a border + cable_shadow = Line(start=self.start, end=self.end, class_='cable-shadow') + self.add(cable_shadow) - # Draw the cable - cable = Line(start=self.start, end=self.end, style=f'stroke: #{self.color}') - self.add(cable) + # Draw the cable + cable = Line(start=self.start, end=self.end, style=f'stroke: #{self.color}') + self.add(cable) # Add link link = Hyperlink(href=url, target='_parent') # Add text label(s) - cursor = start[1] - cursor += PADDING * 2 + cursor = start[1] + text_offset + cursor += PADDING * 2 + LINE_HEIGHT * 2 + x_coord = (start[0] + end[0]) / 2 + PADDING for i, label in enumerate(labels): cursor += LINE_HEIGHT - text_coords = (start[0] + PADDING * 2, cursor - LINE_HEIGHT / 2) + text_coords = (x_coord, cursor - LINE_HEIGHT / 2) text = Text(label, insert=text_coords, class_='bold' if not i else []) link.add(text) if len(description) > 0: @@ -190,8 +196,9 @@ class CableTraceSVG: def draw_parent_objects(self, obj_list): """ - Draw a set of parent objects. + Draw a set of parent objects (eg hosts, switched, patchpanels) and return all created nodes """ + objects = [] width = self.width / len(obj_list) for i, obj in enumerate(obj_list): node = Node( @@ -199,23 +206,26 @@ class CableTraceSVG: width=width, url=f'{self.base_url}{obj.get_absolute_url()}', color=self._get_color(obj), - labels=self._get_labels(obj) + labels=self._get_labels(obj), + object=obj ) + objects.append(node) self.parent_objects.append(node) if i + 1 == len(obj_list): self.cursor += node.box['height'] + return objects - def draw_terminations(self, terminations): + def draw_object_terminations(self, terminations, offset_x, width): """ - Draw a row of terminating objects (e.g. interfaces), all of which are attached to the same end of a cable. + Draw all terminations belonging to an object with specified offset and width + Return all created nodes and their maximum height """ - nodes = [] nodes_height = 0 - width = self.width / len(terminations) - - for i, term in enumerate(terminations): + nodes = [] + # Sort them by name to make renders more readable + for i, term in enumerate(sorted(terminations, key=lambda x: x.name)): node = Node( - position=(i * width, self.cursor), + position=(offset_x + i * width, self.cursor), width=width, url=f'{self.base_url}{term.get_absolute_url()}', color=self._get_color(term), @@ -225,133 +235,93 @@ class CableTraceSVG: ) nodes_height = max(nodes_height, node.box['height']) nodes.append(node) + return nodes, nodes_height + + def draw_terminations(self, terminations, parent_object_nodes): + """ + Draw a row of terminating objects (e.g. interfaces) and return all created nodes + Attach them to previously created parent objects + """ + nodes = [] + nodes_height = 0 + + # Draw terminations for each parent object + for parent in parent_object_nodes: + parent_terms = [term for term in terminations if term.parent_object == parent.object] + + if len(parent_terms) == 0: + self.logger.warn(f"No Parent Terminations? {parent.object.name}") + continue + + # Width and offset(position) for each termination box + width = parent.box['width'] / len(parent_terms) + offset_x = parent.box['x'] + + result, nodes_height = self.draw_object_terminations(parent_terms, offset_x, width) + nodes.extend(result) self.cursor += nodes_height self.terminations.extend(nodes) return nodes - def draw_fanin(self, node, connector): - points = ( - node.bottom_center, - (node.bottom_center[0], node.bottom_center[1] + FANOUT_LEG_HEIGHT), - connector.start, - ) - self.connectors.extend(( - Polyline(points=points, class_='cable-shadow'), - Polyline(points=points, style=f'stroke: #{connector.color}'), - )) - - def draw_fanout(self, node, connector): - points = ( - connector.end, - (node.top_center[0], node.top_center[1] - FANOUT_LEG_HEIGHT), - node.top_center, - ) - self.connectors.extend(( - Polyline(points=points, class_='cable-shadow'), - Polyline(points=points, style=f'stroke: #{connector.color}'), - )) - - def draw_cable(self, cable, terminations, cable_count=0): + def draw_far_objects(self, obj_list, terminations): """ - Draw a single cable. Terminations and cable count are passed for determining position and padding - - :param cable: The cable to draw - :param terminations: List of terminations to build positioning data off of - :param cable_count: Count of all cables on this layer for determining whether to collapse description into a - tooltip. + Draw the far-end objects and its terminations and return all created nodes """ + # Make sure elements are sorted by name for readability + objects = sorted(obj_list, key=lambda x: x.name) + width = self.width / len(objects) - # If the cable count is higher than 2, collapse the description into a tooltip - if cable_count > 2: - # Use the cable __str__ function to denote the cable - labels = [f'{cable}'] + # Max-height of created terminations + terms_height = 0 + term_nodes = [] - # Include the label and the status description in the tooltip - description = [ - f'Cable {cable}', - cable.get_status_display() - ] + # Draw the terminations by per object first + for i, obj in enumerate(objects): + obj_terms = [term for term in terminations if term.parent_object == obj] + obj_pos = i * width + result, result_nodes_height = self.draw_object_terminations(obj_terms, obj_pos, width / len(obj_terms)) - if cable.type: - # Include the cable type in the tooltip - description.append(cable.get_type_display()) - if cable.length is not None and cable.length_unit: - # Include the cable length in the tooltip - description.append(f'{cable.length} {cable.get_length_unit_display()}') - else: - labels = [ - f'Cable {cable}', - cable.get_status_display() - ] - description = [] - if cable.type: - labels.append(cable.get_type_display()) - if cable.length is not None and cable.length_unit: - # Include the cable length in the tooltip - labels.append(f'{cable.length} {cable.get_length_unit_display()}') + terms_height = max(terms_height, result_nodes_height) + term_nodes.extend(result) - # If there is only one termination, center on that termination - # Otherwise average the center across the terminations - if len(terminations) == 1: - center = terminations[0].bottom_center[0] - else: - # Get a list of termination centers - termination_centers = [term.bottom_center[0] for term in terminations] - # Average the centers - center = sum(termination_centers) / len(termination_centers) + # Update cursor and draw the objects + self.cursor += terms_height + self.terminations.extend(term_nodes) + object_nodes = self.draw_parent_objects(objects) - # Create the connector - connector = Connector( - start=(center, self.cursor), - color=cable.color or '000000', - url=f'{self.base_url}{cable.get_absolute_url()}', - labels=labels, - description=description - ) + return object_nodes, term_nodes - # Set the cursor position - self.cursor += connector.height - - return connector - - def draw_wirelesslink(self, wirelesslink): + def draw_fanin(self, target, terminations, color): """ - Draw a line with labels representing a WirelessLink. + Draw the fan-in-lines from each of the terminations to the targetpoint """ - group = Group(class_='connector') + for term in terminations: + points = ( + term.bottom_center, + (term.bottom_center[0], term.bottom_center[1] + FANOUT_LEG_HEIGHT), + target, + ) + self.connectors.extend(( + Polyline(points=points, class_='cable-shadow'), + Polyline(points=points, style=f'stroke: #{color}'), + )) - labels = [ - f'Wireless link {wirelesslink}', - wirelesslink.get_status_display() - ] - if wirelesslink.ssid: - labels.append(wirelesslink.ssid) - - # Draw the wireless link - start = (OFFSET + self.center, self.cursor) - height = PADDING * 2 + LINE_HEIGHT * len(labels) + PADDING * 2 - end = (start[0], start[1] + height) - line = Line(start=start, end=end, class_='wireless-link') - group.add(line) - - self.cursor += PADDING * 2 - - # Add link - link = Hyperlink(href=f'{self.base_url}{wirelesslink.get_absolute_url()}', target='_parent') - - # Add text label(s) - for i, label in enumerate(labels): - self.cursor += LINE_HEIGHT - text_coords = (self.center + PADDING * 2, self.cursor - LINE_HEIGHT / 2) - text = Text(label, insert=text_coords, class_='bold' if not i else []) - link.add(text) - - group.add(link) - self.cursor += PADDING * 2 - - return group + def draw_fanout(self, start, terminations, color): + """ + Draw the fan-out-lines from the startpoint to each of the terminations + """ + for term in terminations: + points = ( + term.top_center, + (term.top_center[0], term.top_center[1] - FANOUT_LEG_HEIGHT), + start, + ) + self.connectors.extend(( + Polyline(points=points, class_='cable-shadow'), + Polyline(points=points, style=f'stroke: #{color}'), + )) def draw_attachment(self): """ @@ -378,86 +348,101 @@ class CableTraceSVG: traced_path = self.origin.trace() + parent_object_nodes = [] # Iterate through each (terms, cable, terms) segment in the path for i, segment in enumerate(traced_path): near_ends, links, far_ends = segment - # Near end parent + # This is segment number one. if i == 0: # If this is the first segment, draw the originating termination's parent object - self.draw_parent_objects(set(end.parent_object for end in near_ends)) + parent_object_nodes = self.draw_parent_objects(set(end.parent_object for end in near_ends)) + # Else: No need to draw parent objects (parent objects are drawn in last "round" as the far-end!) - # Near end termination(s) - terminations = self.draw_terminations(near_ends) + near_terminations = self.draw_terminations(near_ends, parent_object_nodes) + self.cursor += CABLE_HEIGHT # Connector (a Cable or WirelessLink) if links: - link_cables = {} - fanin = False - fanout = False - # Determine if we have fanins or fanouts - if len(near_ends) > len(set(links)): - self.cursor += FANOUT_HEIGHT - fanin = True - if len(far_ends) > len(set(links)): - fanout = True - cursor = self.cursor - for link in links: - # Cable - if type(link) is Cable and not link_cables.get(link.pk): - # Reset cursor - self.cursor = cursor - # Generate a list of terminations connected to this cable - near_end_link_terminations = [term for term in terminations if term.object.cable == link] - # Draw the cable - cable = self.draw_cable(link, near_end_link_terminations, cable_count=len(links)) - # Add cable to the list of cables - link_cables.update({link.pk: cable}) - # Add cable to drawing - self.connectors.append(cable) + parent_object_nodes, far_terminations = self.draw_far_objects(set(end.parent_object for end in far_ends), far_ends) + for cable in links: + # Fill in labels and description with all available data + description = [ + f"Link {cable}", + cable.get_status_display() + ] + near = [] + far = [] + color = '000000' + if cable.description: + description.append(f"{cable.description}") + if cable is Cable: + labels = [f"{cable}"] if len(links) > 2 else [f"Cable {cable}", cable.get_status_display()] + if cable.label: + description.append(f"NetBox ID: {cable.id}") + if cable.type: + description.append(f"Type: {cable.get_type_display()}") + if cable.length: + description.append(f"Length: {cable.length} {cable.length_unit}") + color = cable.color or '000000' - # Draw fan-ins - if len(near_ends) > 1 and fanin: - for term in terminations: - if term.object.cable == link: - self.draw_fanin(term, cable) + # Collect all connected nodes to this cable + near = [term for term in near_terminations if term.object in cable.a_terminations] + far = [term for term in far_terminations if term.object in cable.b_terminations] + elif cable is WirelessLink: + labels = [f"{cable}"] if len(links) > 2 else [f"Wireless {cable}", cable.get_status_display()] + if cable.ssid: + description.append(f"SSID: {cable.ssid}") + if cable.auth_type: + description.append(f"AuthType: {cable.auth_type}") + if cable.auth_cipher: + description.append(f"AuthCipher: {cable.auth_cipher}") + if cable.auth_psk: + description.append(f"PSK is set") + near = [term for term in near_terminations if term.object == cable.interface_a] + far = [term for term in far_terminations if term.object == cable.interface_b] + if cable.tenant: + description.append(f"Tenant: {cable.tenant}") - # WirelessLink - elif type(link) is WirelessLink: - wirelesslink = self.draw_wirelesslink(link) - self.connectors.append(wirelesslink) + # Select most-probable start and end position + start = near[0].bottom_center + end = far[0].top_center + text_offset = 0 - # Far end termination(s) - if len(far_ends) > 1: - if fanout: - self.cursor += FANOUT_HEIGHT - terminations = self.draw_terminations(far_ends) - for term in terminations: - if hasattr(term.object, 'cable') and link_cables.get(term.object.cable.pk): - self.draw_fanout(term, link_cables.get(term.object.cable.pk)) - else: - self.draw_terminations(far_ends) - elif far_ends: - self.draw_terminations(far_ends) - else: - # Link is not connected to anything - break + if len(near) > 1: + # Handle Fan-In - change start position to be directly below start + start = (end[0], start[1] + FANOUT_HEIGHT + FANOUT_LEG_HEIGHT) + self.draw_fanin(start, near, color) + text_offset -= FANOUT_HEIGHT + FANOUT_LEG_HEIGHT + elif len(far) > 1: + # Handle Fan-Out - change end position to be directly above end + end = (start[0], end[1] - FANOUT_HEIGHT - FANOUT_LEG_HEIGHT) + self.draw_fanout(end, far, color) + text_offset -= FANOUT_HEIGHT - # Far end parent - parent_objects = set(end.parent_object for end in far_ends) - self.draw_parent_objects(parent_objects) + # Create the connector + connector = Connector( + start=start, + end=end, + color=color, + wireless=cable is WirelessLink, + url=f'{self.base_url}{cable.get_absolute_url()}', + text_offset=text_offset, + labels=labels, + description=description + ) + self.connectors.append(connector) # Render a far-end object not connected via a link (e.g. a ProviderNetwork or Site associated with # a CircuitTermination) elif far_ends: - # Attachment attachment = self.draw_attachment() self.connectors.append(attachment) # Object - self.draw_parent_objects(far_ends) + parent_object_nodes = self.draw_parent_objects(far_ends) # Determine drawing size self.drawing = svgwrite.Drawing( From ced44832f75913f93a7768e9ee001f1e313bb545 Mon Sep 17 00:00:00 2001 From: Moritz Geist Date: Tue, 9 Jan 2024 14:22:36 +0100 Subject: [PATCH 008/303] Remove dangling logging message used during development --- netbox/dcim/svg/cables.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/netbox/dcim/svg/cables.py b/netbox/dcim/svg/cables.py index 76d6dc68a..7bbb3c2b0 100644 --- a/netbox/dcim/svg/cables.py +++ b/netbox/dcim/svg/cables.py @@ -249,10 +249,6 @@ class CableTraceSVG: for parent in parent_object_nodes: parent_terms = [term for term in terminations if term.parent_object == parent.object] - if len(parent_terms) == 0: - self.logger.warn(f"No Parent Terminations? {parent.object.name}") - continue - # Width and offset(position) for each termination box width = parent.box['width'] / len(parent_terms) offset_x = parent.box['x'] From 2c93dd03e12acd0c9754d6e051166dcdd59d6329 Mon Sep 17 00:00:00 2001 From: Moritz Geist Date: Wed, 10 Jan 2024 14:29:46 +0100 Subject: [PATCH 009/303] account for swapped terminations in cable object also remove out-of-scope changes to tooltips --- netbox/dcim/svg/cables.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/netbox/dcim/svg/cables.py b/netbox/dcim/svg/cables.py index 7bbb3c2b0..596f0c6bf 100644 --- a/netbox/dcim/svg/cables.py +++ b/netbox/dcim/svg/cables.py @@ -373,33 +373,31 @@ class CableTraceSVG: color = '000000' if cable.description: description.append(f"{cable.description}") - if cable is Cable: + if isinstance(cable, Cable): labels = [f"{cable}"] if len(links) > 2 else [f"Cable {cable}", cable.get_status_display()] - if cable.label: - description.append(f"NetBox ID: {cable.id}") if cable.type: - description.append(f"Type: {cable.get_type_display()}") - if cable.length: - description.append(f"Length: {cable.length} {cable.length_unit}") + description.append(cable.get_type_display()) + if cable.length and cable.length_unit: + description.append(f"{cable.length} {cable.get_length_unit_display()}") color = cable.color or '000000' # Collect all connected nodes to this cable near = [term for term in near_terminations if term.object in cable.a_terminations] far = [term for term in far_terminations if term.object in cable.b_terminations] - elif cable is WirelessLink: + if not (near and far): + # a and b terminations may be swapped + near = [term for term in near_terminations if term.object in cable.b_terminations] + far = [term for term in far_terminations if term.object in cable.a_terminations] + elif isinstance(cable, WirelessLink): labels = [f"{cable}"] if len(links) > 2 else [f"Wireless {cable}", cable.get_status_display()] if cable.ssid: - description.append(f"SSID: {cable.ssid}") - if cable.auth_type: - description.append(f"AuthType: {cable.auth_type}") - if cable.auth_cipher: - description.append(f"AuthCipher: {cable.auth_cipher}") - if cable.auth_psk: - description.append(f"PSK is set") + description.append(f"{cable.ssid}") near = [term for term in near_terminations if term.object == cable.interface_a] far = [term for term in far_terminations if term.object == cable.interface_b] - if cable.tenant: - description.append(f"Tenant: {cable.tenant}") + if not (near and far): + # a and b terminations may be swapped + near = [term for term in near_terminations if term.object == cable.interface_b] + far = [term for term in far_terminations if term.object == cable.interface_a] # Select most-probable start and end position start = near[0].bottom_center @@ -422,7 +420,7 @@ class CableTraceSVG: start=start, end=end, color=color, - wireless=cable is WirelessLink, + wireless=isinstance(cable, WirelessLink), url=f'{self.base_url}{cable.get_absolute_url()}', text_offset=text_offset, labels=labels, From da7f67c35951d54d7d6c318fe134574538aa7ec5 Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Tue, 23 Jan 2024 20:49:10 +0100 Subject: [PATCH 010/303] Refactor noisy getter methods into neat lambdas --- netbox/dcim/tables/devices.py | 48 +++-------------------------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 063e05215..fcacd886a 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -52,46 +52,6 @@ def get_cabletermination_row_class(record): return '' -def get_interface_state_attribute(record): - """ - Get interface enabled state as string to attach to DOM element. - """ - if record.enabled: - return "enabled" - else: - return "disabled" - - -def get_interface_virtual_attribute(record): - """ - Get interface virtual state as string to attach to DOM element. - """ - if record.is_virtual: - return "true" - else: - return "false" - - -def get_interface_mark_connected_attribute(record): - """ - Get interface enabled state as string to attach to DOM element. - """ - if record.mark_connected: - return "true" - else: - return "false" - - -def get_interface_cable_status_attribute(record): - """ - Get interface enabled state as string to attach to DOM element. - """ - if record.cable: - return record.cable.status - else: - return "" - - # # Device roles # @@ -694,10 +654,10 @@ class DeviceInterfaceTable(InterfaceTable): ) row_attrs = { 'data-name': lambda record: record.name, - 'data-enabled': get_interface_state_attribute, - 'data-virtual': get_interface_virtual_attribute, - 'data-mark-connected': get_interface_mark_connected_attribute, - 'data-cable-status': get_interface_cable_status_attribute, + 'data-enabled': lambda record: "enabled" if record.enabled else "disabled", + 'data-virtual': lambda record: "true" if record.is_virtual else "false", + 'data-mark-connected': lambda record: "true" if record.mark_connected else "false", + 'data-cable-status': lambda record: record.cable.status if record.cable else "", 'data-type': lambda record: record.type, } cable_status_styles = [(slug, color) for slug, _, color in LinkStatusChoices.CHOICES] From bf362f4679f9d9c30519ea1ce80076c59a79e8c2 Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Tue, 23 Jan 2024 20:58:10 +0100 Subject: [PATCH 011/303] Hardcode cable status colours --- netbox/dcim/tables/devices.py | 2 -- netbox/templates/dcim/device/interfaces.html | 14 +++++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index fcacd886a..d15cfe64d 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -6,7 +6,6 @@ from dcim import models from netbox.tables import NetBoxTable, columns from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin from .template_code import * -from dcim.choices import LinkStatusChoices __all__ = ( 'BaseInterfaceTable', @@ -660,7 +659,6 @@ class DeviceInterfaceTable(InterfaceTable): 'data-cable-status': lambda record: record.cable.status if record.cable else "", 'data-type': lambda record: record.type, } - cable_status_styles = [(slug, color) for slug, _, color in LinkStatusChoices.CHOICES] class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable): diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index 8669789c7..9860d74ef 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -34,11 +34,15 @@ {% block head %} {{ block.super }} -{% endblock %} From 83dad6f7717f7de3e4dec778ebd4a6fb2f16c89c Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 3 Apr 2024 09:25:03 -0700 Subject: [PATCH 013/303] 15597 add button_class choices to import form --- netbox/extras/forms/bulk_import.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py index 59ccc2bf9..2baf373c0 100644 --- a/netbox/extras/forms/bulk_import.py +++ b/netbox/extras/forms/bulk_import.py @@ -116,6 +116,12 @@ class CustomLinkImportForm(CSVModelForm): queryset=ContentType.objects.with_feature('custom_links'), help_text=_("One or more assigned object types") ) + button_class = CSVChoiceField( + label=_('button class'), + required=False, + choices=CustomLinkButtonClassChoices, + help_text=_('The class of the first link in a group will be used for the dropdown button') + ) class Meta: model = CustomLink From 6030c521f45317ffa39f626fe37684320c84a04a Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Thu, 14 Mar 2024 17:15:35 +0100 Subject: [PATCH 014/303] Fix typo in Add Components dropdown --- netbox/templates/dcim/device/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/dcim/device/base.html b/netbox/templates/dcim/device/base.html index 43d5c5ae9..90cb829ac 100644 --- a/netbox/templates/dcim/device/base.html +++ b/netbox/templates/dcim/device/base.html @@ -42,7 +42,7 @@ {% if perms.dcim.add_rearport %}
  • {% trans "Rear Ports" %}
  • {% endif %} - {% if perms.dcim.add_devicebay %} + {% if perms.dcim.add_modulebay %}
  • {% trans "Module Bays" %}
  • {% endif %} {% if perms.dcim.add_devicebay %} From e4984d288369056650c92301c9f740bd2d3fe429 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Fri, 9 Feb 2024 22:57:38 +0530 Subject: [PATCH 015/303] fixed user and group filter form name #15102 --- netbox/users/forms/filtersets.py | 8 ++++---- netbox/users/views.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/netbox/users/forms/filtersets.py b/netbox/users/forms/filtersets.py index 4ae2bd729..0e9e99a83 100644 --- a/netbox/users/forms/filtersets.py +++ b/netbox/users/forms/filtersets.py @@ -11,21 +11,21 @@ from utilities.forms.fields import DynamicModelMultipleChoiceField from utilities.forms.widgets import DateTimePicker __all__ = ( - 'GroupFilterForm', + 'NetBoxGroupFilterForm', 'ObjectPermissionFilterForm', - 'UserFilterForm', + 'NetBoxUserFilterForm', 'TokenFilterForm', ) -class GroupFilterForm(NetBoxModelFilterSetForm): +class NetBoxGroupFilterForm(NetBoxModelFilterSetForm): model = NetBoxGroup fieldsets = ( (None, ('q', 'filter_id',)), ) -class UserFilterForm(NetBoxModelFilterSetForm): +class NetBoxUserFilterForm(NetBoxModelFilterSetForm): model = NetBoxUser fieldsets = ( (None, ('q', 'filter_id',)), diff --git a/netbox/users/views.py b/netbox/users/views.py index 2e7a47c12..e0fda403e 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -58,7 +58,7 @@ class TokenBulkDeleteView(generic.BulkDeleteView): class UserListView(generic.ObjectListView): queryset = NetBoxUser.objects.all() filterset = filtersets.UserFilterSet - filterset_form = forms.UserFilterForm + filterset_form = forms.NetBoxUserFilterForm table = tables.UserTable @@ -112,7 +112,7 @@ class UserBulkDeleteView(generic.BulkDeleteView): class GroupListView(generic.ObjectListView): queryset = NetBoxGroup.objects.annotate(users_count=Count('user')) filterset = filtersets.GroupFilterSet - filterset_form = forms.GroupFilterForm + filterset_form = forms.NetBoxGroupFilterForm table = tables.GroupTable From fca23c6419f7027dadf842f03e4de8c43323fd18 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 5 Feb 2024 10:37:01 -0800 Subject: [PATCH 016/303] 15029 check if duplicate FHRP group assignment --- netbox/ipam/forms/model_forms.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index 71aa32d52..a0e004298 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -507,6 +507,24 @@ class FHRPGroupAssignmentForm(BootstrapMixin, forms.ModelForm): for ipaddress in ipaddresses: self.fields['group'].widget.add_query_param('related_ip', ipaddress.pk) + def clean_group(self): + group = self.cleaned_data['group'] + + conflicting_assignments = FHRPGroupAssignment.objects.filter( + interface_type=self.instance.interface_type, + interface_id=self.instance.interface_id, + group=group + ) + if self.instance.id: + conflicting_assignments = conflicting_assignments.exclude(id=self.instance.id) + + if conflicting_assignments.exists(): + raise forms.ValidationError( + _('Assignment already exists') + ) + + return group + class VLANGroupForm(NetBoxModelForm): scope_type = ContentTypeChoiceField( From 24e2fc253ab4ce529bebc189bd17069b1916560a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 3 Apr 2024 14:12:35 -0400 Subject: [PATCH 017/303] Changelog for #15029, #15102, #15435, #15597 --- docs/release-notes/version-3.7.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index 0e4bcfc5b..906b27ef3 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -5,7 +5,11 @@ ### Bug Fixes * [#14799](https://github.com/netbox-community/netbox/issues/14799) - Avoid caching modified reports & scripts +* [#15029](https://github.com/netbox-community/netbox/issues/15029) - Raise a clean validation error when attempting to make duplicate FHRP group assignments +* [#15102](https://github.com/netbox-community/netbox/issues/15102) - Fix usage of selector widget for form fields referencing users/groups +* [#15435](https://github.com/netbox-community/netbox/issues/15435) - Correct permissions name to allow adding a module bay to a device via the UI * [#15502](https://github.com/netbox-community/netbox/issues/15502) - Fix KeyError exception when modifying an IP address assigned to a virtual machine +* [#15597](https://github.com/netbox-community/netbox/issues/15597) - Restore help modal for `button_class` field on custom link bulk import form --- From 1c370f45d06dabf78072e73949bd5f2494687563 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Apr 2024 09:20:20 -0400 Subject: [PATCH 018/303] Add weighted assignments & enable for documentation issue --- .github/workflows/auto-assign-issue.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-assign-issue.yml b/.github/workflows/auto-assign-issue.yml index 20e054806..7a9f857a9 100644 --- a/.github/workflows/auto-assign-issue.yml +++ b/.github/workflows/auto-assign-issue.yml @@ -13,8 +13,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: pozil/auto-assign-issue@v1 - if: "contains(github.event.issue.labels.*.name, 'type: bug') || contains(github.event.issue.labels.*.name, 'type: feature')" + if: > + contains(github.event.issue.labels.*.name, 'type: bug') || + contains(github.event.issue.labels.*.name, 'type: feature') || + contains(github.event.issue.labels.*.name, 'type: documentation') with: - assignees: abhi1693,arthanson,DanSheps,jeffgdotorg,jeremystretch + # Weighted assignments + assignees: arthanson:3, jeffgdotorg:3, jeremystretch:3, abhi1693, DanSheps numOfAssignee: 1 abortIfPreviousAssignees: true From 0e94f2e05d871c13afb5a0f9da45778e9e390a52 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Apr 2024 09:53:49 -0400 Subject: [PATCH 019/303] Simplify auto-assignment qualification --- .github/workflows/auto-assign-issue.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/auto-assign-issue.yml b/.github/workflows/auto-assign-issue.yml index 7a9f857a9..3d0330dc9 100644 --- a/.github/workflows/auto-assign-issue.yml +++ b/.github/workflows/auto-assign-issue.yml @@ -13,10 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: pozil/auto-assign-issue@v1 - if: > - contains(github.event.issue.labels.*.name, 'type: bug') || - contains(github.event.issue.labels.*.name, 'type: feature') || - contains(github.event.issue.labels.*.name, 'type: documentation') + if: contains(github.event.issue.labels.*.name, 'needs triage') with: # Weighted assignments assignees: arthanson:3, jeffgdotorg:3, jeremystretch:3, abhi1693, DanSheps From e1753c0f9b15822b3fd2683124d8e8f53484f812 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Apr 2024 10:03:12 -0400 Subject: [PATCH 020/303] Fix formatting --- .github/workflows/auto-assign-issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-assign-issue.yml b/.github/workflows/auto-assign-issue.yml index 3d0330dc9..9abbc0cce 100644 --- a/.github/workflows/auto-assign-issue.yml +++ b/.github/workflows/auto-assign-issue.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: pozil/auto-assign-issue@v1 - if: contains(github.event.issue.labels.*.name, 'needs triage') + if: "contains(github.event.issue.labels.*.name, 'needs triage')" with: # Weighted assignments assignees: arthanson:3, jeffgdotorg:3, jeremystretch:3, abhi1693, DanSheps From 282dc7a705240b29838731869a8ed2acef53b2b6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 3 Apr 2024 16:18:34 -0400 Subject: [PATCH 021/303] Fixes #15608: Avoid caching values of null fields in search index --- netbox/netbox/search/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/netbox/netbox/search/__init__.py b/netbox/netbox/search/__init__.py index 590188f21..7537c0419 100644 --- a/netbox/netbox/search/__init__.py +++ b/netbox/netbox/search/__init__.py @@ -59,9 +59,10 @@ class SearchIndex: @staticmethod def get_field_value(instance, field_name): """ - Return the value of the specified model field as a string. + Return the value of the specified model field as a string (or None). """ - return str(getattr(instance, field_name)) + if value := getattr(instance, field_name): + return str(value) @classmethod def get_category(cls): From d9a7b4ee0ebb225b5239306ec94b6f369956be9b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 3 Apr 2024 16:04:41 -0400 Subject: [PATCH 022/303] Fixes #15609: Fix filtering providers list by assigned ASN --- netbox/circuits/filtersets.py | 6 ++++++ netbox/circuits/forms/filtersets.py | 6 +----- netbox/circuits/tests/test_filtersets.py | 4 +++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/netbox/circuits/filtersets.py b/netbox/circuits/filtersets.py index 97be1cf57..9a14b5286 100644 --- a/netbox/circuits/filtersets.py +++ b/netbox/circuits/filtersets.py @@ -64,6 +64,12 @@ class ProviderFilterSet(NetBoxModelFilterSet, ContactModelFilterSet): queryset=ASN.objects.all(), label=_('ASN (ID)'), ) + asn = django_filters.ModelMultipleChoiceFilter( + field_name='asns__asn', + queryset=ASN.objects.all(), + to_field_name='asn', + label=_('ASN'), + ) class Meta: model = Provider diff --git a/netbox/circuits/forms/filtersets.py b/netbox/circuits/forms/filtersets.py index 1e1abd068..8f730ba10 100644 --- a/netbox/circuits/forms/filtersets.py +++ b/netbox/circuits/forms/filtersets.py @@ -24,7 +24,7 @@ class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm): fieldsets = ( (None, ('q', 'filter_id', 'tag')), (_('Location'), ('region_id', 'site_group_id', 'site_id')), - (_('ASN'), ('asn',)), + (_('ASN'), ('asn_id',)), (_('Contacts'), ('contact', 'contact_role', 'contact_group')), ) region_id = DynamicModelMultipleChoiceField( @@ -46,10 +46,6 @@ class ProviderFilterForm(ContactModelFilterForm, NetBoxModelFilterSetForm): }, label=_('Site') ) - asn = forms.IntegerField( - required=False, - label=_('ASN (legacy)') - ) asn_id = DynamicModelMultipleChoiceField( queryset=ASN.objects.all(), required=False, diff --git a/netbox/circuits/tests/test_filtersets.py b/netbox/circuits/tests/test_filtersets.py index 6553179ec..ae9de03e9 100644 --- a/netbox/circuits/tests/test_filtersets.py +++ b/netbox/circuits/tests/test_filtersets.py @@ -90,10 +90,12 @@ class ProviderTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'description': ['foobar1', 'foobar2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) - def test_asn_id(self): # ASN object assignment + def test_asn(self): asns = ASN.objects.all()[:2] params = {'asn_id': [asns[0].pk, asns[1].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'asn': [asns[0].asn, asns[1].asn]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_region(self): regions = Region.objects.all()[:2] From 5b50920c618d20a770ddfce217b974cefbd28212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markku=20Leini=C3=B6?= Date: Thu, 4 Apr 2024 18:46:01 +0300 Subject: [PATCH 023/303] Closes #14707: Change 'Interface' to 'Tunnel interface' in VPN tunnel forms --- netbox/vpn/forms/model_forms.py | 6 +++--- netbox/vpn/tables/tunnels.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/netbox/vpn/forms/model_forms.py b/netbox/vpn/forms/model_forms.py index f936de88c..adf358be7 100644 --- a/netbox/vpn/forms/model_forms.py +++ b/netbox/vpn/forms/model_forms.py @@ -91,7 +91,7 @@ class TunnelCreateForm(TunnelForm): termination1_termination = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, - label=_('Interface'), + label=_('Tunnel interface'), query_params={ 'device_id': '$termination1_parent', } @@ -126,7 +126,7 @@ class TunnelCreateForm(TunnelForm): termination2_termination = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, - label=_('Interface'), + label=_('Tunnel interface'), query_params={ 'device_id': '$termination2_parent', } @@ -238,7 +238,7 @@ class TunnelTerminationForm(NetBoxModelForm): ) termination = DynamicModelChoiceField( queryset=Interface.objects.all(), - label=_('Interface'), + label=_('Tunnel interface'), query_params={ 'device_id': '$parent', } diff --git a/netbox/vpn/tables/tunnels.py b/netbox/vpn/tables/tunnels.py index bc591c1e6..cf4230652 100644 --- a/netbox/vpn/tables/tunnels.py +++ b/netbox/vpn/tables/tunnels.py @@ -88,7 +88,7 @@ class TunnelTerminationTable(TenancyColumnsMixin, NetBoxTable): verbose_name=_('Host') ) termination = tables.Column( - verbose_name=_('Interface'), + verbose_name=_('Tunnel interface'), linkify=True ) ip_addresses = tables.ManyToManyColumn( From da13fa55694eaaaf2a99069815afc0b24bc19c84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markku=20Leini=C3=B6?= Date: Thu, 4 Apr 2024 19:17:57 +0300 Subject: [PATCH 024/303] Closes #15039: Add Clone button in API token --- netbox/users/models.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/netbox/users/models.py b/netbox/users/models.py index 52ce55e6c..160f49ac4 100644 --- a/netbox/users/models.py +++ b/netbox/users/models.py @@ -17,6 +17,7 @@ from netaddr import IPNetwork from core.models import ContentType from ipam.fields import IPNetworkField from netbox.config import get_config +from netbox.models.features import CloningMixin from utilities.querysets import RestrictedQuerySet from utilities.utils import flatten_dict from .constants import * @@ -234,7 +235,7 @@ def create_userconfig(instance, created, raw=False, **kwargs): # REST API # -class Token(models.Model): +class Token(CloningMixin, models.Model): """ An API token used for user authentication. This extends the stock model to allow each user to have multiple tokens. It also supports setting an expiration time and toggling write ability. @@ -285,6 +286,10 @@ class Token(models.Model): ), ) + clone_fields = ( + 'user', 'expires', 'write_enabled', 'description', 'allowed_ips', + ) + objects = RestrictedQuerySet.as_manager() class Meta: From 3b3511c43c0c8565d846817e5d6233f1ef610767 Mon Sep 17 00:00:00 2001 From: padthaitofuhot Date: Tue, 2 Apr 2024 23:30:13 -0400 Subject: [PATCH 025/303] Refactor 32264ac3 to re-separate bulk and single device creation. Fixes #15598. --- netbox/dcim/models/devices.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 2a1269a8d..588901709 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -996,17 +996,16 @@ class Device( bulk_create: If True, bulk_create() will be called to create all components in a single query (default). Otherwise, save() will be called on each instance individually. """ - components = [obj.instantiate(device=self) for obj in queryset] - if not components: - return - - # Set default values for any applicable custom fields model = queryset.model.component_model - if cf_defaults := CustomField.objects.get_defaults_for_model(model): - for component in components: - component.custom_field_data = cf_defaults if bulk_create: + components = [obj.instantiate(device=self) for obj in queryset] + if not components: + return + # Set default values for any applicable custom fields + if cf_defaults := CustomField.objects.get_defaults_for_model(model): + for component in components: + component.custom_field_data = cf_defaults model.objects.bulk_create(components) # Manually send the post_save signal for each of the newly created components for component in components: @@ -1019,7 +1018,11 @@ class Device( update_fields=None ) else: - for component in components: + for obj in queryset: + component = obj.instantiate(device=self) + # Set default values for any applicable custom fields + if cf_defaults := CustomField.objects.get_defaults_for_model(model): + component.custom_field_data = cf_defaults component.save() def save(self, *args, **kwargs): From 238fa704b9c5a684e13ca3070fa32ce4bdd7dea2 Mon Sep 17 00:00:00 2001 From: muTeREdO <133774274+muTeREdO@users.noreply.github.com> Date: Thu, 4 Apr 2024 14:21:26 -0400 Subject: [PATCH 026/303] add example showing how to order results. (#15627) * add example showing how to order results. This addresses issue 15622 by building off filtering example to show how to order results on a named field. * Apply suggestions from code review --------- Co-authored-by: Frank Clements Co-authored-by: Jeremy Stretch --- docs/integrations/rest-api.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/integrations/rest-api.md b/docs/integrations/rest-api.md index 437f652b7..1ba00958f 100644 --- a/docs/integrations/rest-api.md +++ b/docs/integrations/rest-api.md @@ -85,13 +85,19 @@ Each model generally has two views associated with it: a list view and a detail * `/api/dcim/devices/` - List existing devices or create a new device * `/api/dcim/devices/123/` - Retrieve, update, or delete the device with ID 123 -Lists of objects can be filtered using a set of query parameters. For example, to find all interfaces belonging to the device with ID 123: +Lists of objects can be filtered and ordered using a set of query parameters. For example, to find all interfaces belonging to the device with ID 123: ``` GET /api/dcim/interfaces/?device_id=123 ``` -See the [filtering documentation](../reference/filtering.md) for more details. +An optional `ordering` parameter can be used to define how to sort the results. Building off the previous example, to sort all the interfaces in reverse order of creation (newest to oldest) for a device with ID 123: + +``` +GET /api/dcim/interfaces/?device_id=123&ordering=-created +``` + +See the [filtering documentation](../reference/filtering.md) for more details on topics related to filtering, ordering and lookup expressions. ## Serialization From 48a3f3cb70a4dcaeb3ad26495416f5cc2b207051 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Apr 2024 15:05:49 -0400 Subject: [PATCH 027/303] Changelog for #14707, #15039, #15598, #15608, #15609 --- docs/release-notes/version-3.7.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index 906b27ef3..15159b4f3 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -2,6 +2,11 @@ ## v3.7.5 (FUTURE) +### Enhancements + +* [#14707](https://github.com/netbox-community/netbox/issues/14707) - Clarify interface designation when creating tunnel terminations +* [#15039](https://github.com/netbox-community/netbox/issues/15039) - Allow API tokens to be cloned + ### Bug Fixes * [#14799](https://github.com/netbox-community/netbox/issues/14799) - Avoid caching modified reports & scripts @@ -10,6 +15,9 @@ * [#15435](https://github.com/netbox-community/netbox/issues/15435) - Correct permissions name to allow adding a module bay to a device via the UI * [#15502](https://github.com/netbox-community/netbox/issues/15502) - Fix KeyError exception when modifying an IP address assigned to a virtual machine * [#15597](https://github.com/netbox-community/netbox/issues/15597) - Restore help modal for `button_class` field on custom link bulk import form +* [#15598](https://github.com/netbox-community/netbox/issues/15598) - Fix exception when creating a device from a device type with one or more child inventory items +* [#15608](https://github.com/netbox-community/netbox/issues/15608) - Avoid caching values of null fields in search index +* [#15609](https://github.com/netbox-community/netbox/issues/15609) - Fix filtering of the providers list by assigned ASN --- From e10f5ec3b4f93f43ba794de6d3f89bbd1f2cb76f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Apr 2024 15:12:51 -0400 Subject: [PATCH 028/303] Update source strings for translation --- netbox/translations/en/LC_MESSAGES/django.po | 790 ++++++++++--------- 1 file changed, 401 insertions(+), 389 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index 80d8b317a..dfb5a7a59 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-21 19:48+0000\n" +"POT-Creation-Date: 2024-04-04 19:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -60,8 +60,8 @@ msgid "Your preferences have been updated." msgstr "" #: circuits/choices.py:21 dcim/choices.py:20 dcim/choices.py:102 -#: dcim/choices.py:174 dcim/choices.py:220 dcim/choices.py:1419 -#: dcim/choices.py:1495 dcim/choices.py:1545 virtualization/choices.py:20 +#: dcim/choices.py:174 dcim/choices.py:220 dcim/choices.py:1425 +#: dcim/choices.py:1501 dcim/choices.py:1551 virtualization/choices.py:20 #: virtualization/choices.py:45 vpn/choices.py:18 msgid "Planned" msgstr "" @@ -71,8 +71,8 @@ msgid "Provisioning" msgstr "" #: circuits/choices.py:23 dcim/choices.py:22 dcim/choices.py:103 -#: dcim/choices.py:173 dcim/choices.py:219 dcim/choices.py:1494 -#: dcim/choices.py:1544 extras/tables/tables.py:380 ipam/choices.py:31 +#: dcim/choices.py:173 dcim/choices.py:219 dcim/choices.py:1500 +#: dcim/choices.py:1550 extras/tables/tables.py:380 ipam/choices.py:31 #: ipam/choices.py:49 ipam/choices.py:69 ipam/choices.py:154 #: templates/extras/configcontext.html:26 templates/users/user.html:38 #: users/forms/bulk_edit.py:36 virtualization/choices.py:22 @@ -81,7 +81,7 @@ msgid "Active" msgstr "" #: circuits/choices.py:24 dcim/choices.py:172 dcim/choices.py:218 -#: dcim/choices.py:1493 dcim/choices.py:1546 virtualization/choices.py:24 +#: dcim/choices.py:1499 dcim/choices.py:1552 virtualization/choices.py:24 #: virtualization/choices.py:43 msgid "Offline" msgstr "" @@ -94,7 +94,7 @@ msgstr "" msgid "Decommissioned" msgstr "" -#: circuits/filtersets.py:29 circuits/filtersets.py:184 dcim/filtersets.py:124 +#: circuits/filtersets.py:29 circuits/filtersets.py:190 dcim/filtersets.py:124 #: dcim/filtersets.py:185 dcim/filtersets.py:260 dcim/filtersets.py:369 #: dcim/filtersets.py:903 dcim/filtersets.py:1207 dcim/filtersets.py:1702 #: dcim/filtersets.py:1945 dcim/filtersets.py:2003 ipam/filtersets.py:305 @@ -103,7 +103,7 @@ msgstr "" msgid "Region (ID)" msgstr "" -#: circuits/filtersets.py:36 circuits/filtersets.py:191 dcim/filtersets.py:130 +#: circuits/filtersets.py:36 circuits/filtersets.py:197 dcim/filtersets.py:130 #: dcim/filtersets.py:192 dcim/filtersets.py:267 dcim/filtersets.py:376 #: dcim/filtersets.py:910 dcim/filtersets.py:1214 dcim/filtersets.py:1709 #: dcim/filtersets.py:1952 dcim/filtersets.py:2010 extras/filtersets.py:414 @@ -113,7 +113,7 @@ msgstr "" msgid "Region (slug)" msgstr "" -#: circuits/filtersets.py:42 circuits/filtersets.py:197 dcim/filtersets.py:198 +#: circuits/filtersets.py:42 circuits/filtersets.py:203 dcim/filtersets.py:198 #: dcim/filtersets.py:273 dcim/filtersets.py:382 dcim/filtersets.py:916 #: dcim/filtersets.py:1220 dcim/filtersets.py:1715 dcim/filtersets.py:1958 #: dcim/filtersets.py:2016 ipam/filtersets.py:318 ipam/filtersets.py:909 @@ -121,7 +121,7 @@ msgstr "" msgid "Site group (ID)" msgstr "" -#: circuits/filtersets.py:49 circuits/filtersets.py:204 dcim/filtersets.py:205 +#: circuits/filtersets.py:49 circuits/filtersets.py:210 dcim/filtersets.py:205 #: dcim/filtersets.py:280 dcim/filtersets.py:389 dcim/filtersets.py:923 #: dcim/filtersets.py:1227 dcim/filtersets.py:1722 dcim/filtersets.py:1965 #: dcim/filtersets.py:2023 extras/filtersets.py:420 ipam/filtersets.py:325 @@ -131,7 +131,7 @@ msgid "Site group (slug)" msgstr "" #: circuits/filtersets.py:54 circuits/forms/bulk_import.py:117 -#: circuits/forms/filtersets.py:47 circuits/forms/filtersets.py:171 +#: circuits/forms/filtersets.py:47 circuits/forms/filtersets.py:167 #: circuits/forms/model_forms.py:137 dcim/forms/bulk_edit.py:166 #: dcim/forms/bulk_edit.py:238 dcim/forms/bulk_edit.py:570 #: dcim/forms/bulk_edit.py:763 dcim/forms/bulk_import.py:130 @@ -154,8 +154,8 @@ msgstr "" #: ipam/forms/bulk_import.py:170 ipam/forms/bulk_import.py:437 #: ipam/forms/filtersets.py:152 ipam/forms/filtersets.py:226 #: ipam/forms/filtersets.py:417 ipam/forms/filtersets.py:470 -#: ipam/forms/model_forms.py:206 ipam/forms/model_forms.py:548 -#: ipam/forms/model_forms.py:640 ipam/tables/ip.py:244 ipam/tables/vlans.py:114 +#: ipam/forms/model_forms.py:206 ipam/forms/model_forms.py:552 +#: ipam/forms/model_forms.py:644 ipam/tables/ip.py:244 ipam/tables/vlans.py:114 #: ipam/tables/vlans.py:216 templates/circuits/circuittermination_edit.html:20 #: templates/circuits/inc/circuit_termination.html:33 #: templates/dcim/device.html:22 templates/dcim/inc/cable_termination.html:8 @@ -172,13 +172,13 @@ msgstr "" #: virtualization/forms/model_forms.py:74 #: virtualization/forms/model_forms.py:107 #: virtualization/forms/model_forms.py:174 virtualization/tables/clusters.py:77 -#: virtualization/tables/virtualmachines.py:53 vpn/forms/filtersets.py:262 +#: virtualization/tables/virtualmachines.py:62 vpn/forms/filtersets.py:262 #: wireless/forms/model_forms.py:77 wireless/forms/model_forms.py:117 msgid "Site" msgstr "" -#: circuits/filtersets.py:60 circuits/filtersets.py:215 -#: circuits/filtersets.py:252 dcim/filtersets.py:215 dcim/filtersets.py:290 +#: circuits/filtersets.py:60 circuits/filtersets.py:221 +#: circuits/filtersets.py:258 dcim/filtersets.py:215 dcim/filtersets.py:290 #: dcim/filtersets.py:363 extras/filtersets.py:436 ipam/filtersets.py:215 #: ipam/filtersets.py:335 ipam/filtersets.py:926 #: virtualization/filtersets.py:75 virtualization/filtersets.py:203 @@ -190,33 +190,39 @@ msgstr "" msgid "ASN (ID)" msgstr "" -#: circuits/filtersets.py:87 circuits/filtersets.py:114 -#: circuits/filtersets.py:148 -msgid "Provider (ID)" +#: circuits/filtersets.py:71 circuits/forms/filtersets.py:27 +#: ipam/forms/model_forms.py:158 ipam/models/asns.py:108 +#: ipam/models/asns.py:125 ipam/tables/asn.py:41 templates/ipam/asn.html:20 +msgid "ASN" msgstr "" #: circuits/filtersets.py:93 circuits/filtersets.py:120 #: circuits/filtersets.py:154 +msgid "Provider (ID)" +msgstr "" + +#: circuits/filtersets.py:99 circuits/filtersets.py:126 +#: circuits/filtersets.py:160 msgid "Provider (slug)" msgstr "" -#: circuits/filtersets.py:159 +#: circuits/filtersets.py:165 msgid "Provider account (ID)" msgstr "" -#: circuits/filtersets.py:164 +#: circuits/filtersets.py:170 msgid "Provider network (ID)" msgstr "" -#: circuits/filtersets.py:168 +#: circuits/filtersets.py:174 msgid "Circuit type (ID)" msgstr "" -#: circuits/filtersets.py:174 +#: circuits/filtersets.py:180 msgid "Circuit type (slug)" msgstr "" -#: circuits/filtersets.py:209 circuits/filtersets.py:246 dcim/filtersets.py:209 +#: circuits/filtersets.py:215 circuits/filtersets.py:252 dcim/filtersets.py:209 #: dcim/filtersets.py:284 dcim/filtersets.py:357 dcim/filtersets.py:927 #: dcim/filtersets.py:1232 dcim/filtersets.py:1727 dcim/filtersets.py:1969 #: dcim/filtersets.py:2028 ipam/filtersets.py:209 ipam/filtersets.py:329 @@ -225,13 +231,13 @@ msgstr "" msgid "Site (ID)" msgstr "" -#: circuits/filtersets.py:238 core/filtersets.py:73 core/filtersets.py:132 +#: circuits/filtersets.py:244 core/filtersets.py:73 core/filtersets.py:132 #: dcim/filtersets.py:640 dcim/filtersets.py:1201 dcim/filtersets.py:2076 #: extras/filtersets.py:40 extras/filtersets.py:69 extras/filtersets.py:101 #: extras/filtersets.py:140 extras/filtersets.py:168 extras/filtersets.py:195 #: extras/filtersets.py:226 extras/filtersets.py:295 extras/filtersets.py:343 #: extras/filtersets.py:403 extras/filtersets.py:562 extras/filtersets.py:604 -#: extras/filtersets.py:645 ipam/forms/model_forms.py:430 +#: extras/filtersets.py:645 ipam/forms/model_forms.py:416 #: netbox/filtersets.py:275 netbox/forms/__init__.py:23 #: netbox/forms/base.py:163 templates/htmx/object_selector.html:28 #: templates/inc/filter_list.html:53 templates/ipam/ipaddress_assign.html:32 @@ -241,7 +247,7 @@ msgstr "" msgid "Search" msgstr "" -#: circuits/filtersets.py:242 circuits/forms/bulk_edit.py:167 +#: circuits/filtersets.py:248 circuits/forms/bulk_edit.py:167 #: circuits/forms/model_forms.py:110 circuits/forms/model_forms.py:132 #: dcim/forms/connections.py:66 templates/circuits/circuit.html:15 #: templates/dcim/inc/cable_termination.html:55 @@ -249,11 +255,11 @@ msgstr "" msgid "Circuit" msgstr "" -#: circuits/filtersets.py:256 +#: circuits/filtersets.py:262 msgid "ProviderNetwork (ID)" msgstr "" -#: circuits/forms/bulk_edit.py:25 circuits/forms/filtersets.py:56 +#: circuits/forms/bulk_edit.py:25 circuits/forms/filtersets.py:52 #: circuits/forms/model_forms.py:26 circuits/tables/providers.py:33 #: dcim/forms/bulk_edit.py:126 dcim/forms/filtersets.py:187 #: dcim/forms/model_forms.py:126 dcim/tables/sites.py:94 @@ -360,8 +366,8 @@ msgstr "" #: circuits/forms/bulk_edit.py:46 circuits/forms/bulk_edit.py:68 #: circuits/forms/bulk_edit.py:118 circuits/forms/bulk_import.py:35 #: circuits/forms/bulk_import.py:50 circuits/forms/bulk_import.py:76 -#: circuits/forms/filtersets.py:70 circuits/forms/filtersets.py:88 -#: circuits/forms/filtersets.py:116 circuits/forms/filtersets.py:131 +#: circuits/forms/filtersets.py:66 circuits/forms/filtersets.py:84 +#: circuits/forms/filtersets.py:112 circuits/forms/filtersets.py:127 #: circuits/forms/model_forms.py:32 circuits/forms/model_forms.py:44 #: circuits/forms/model_forms.py:58 circuits/forms/model_forms.py:92 #: circuits/tables/circuits.py:55 circuits/tables/providers.py:72 @@ -373,18 +379,18 @@ msgstr "" msgid "Provider" msgstr "" -#: circuits/forms/bulk_edit.py:75 circuits/forms/filtersets.py:91 +#: circuits/forms/bulk_edit.py:75 circuits/forms/filtersets.py:87 #: templates/circuits/providernetwork.html:31 msgid "Service ID" msgstr "" -#: circuits/forms/bulk_edit.py:95 circuits/forms/filtersets.py:107 +#: circuits/forms/bulk_edit.py:95 circuits/forms/filtersets.py:103 #: dcim/forms/bulk_edit.py:204 dcim/forms/bulk_edit.py:500 #: dcim/forms/bulk_edit.py:694 dcim/forms/bulk_edit.py:1063 #: dcim/forms/bulk_edit.py:1090 dcim/forms/bulk_edit.py:1562 #: dcim/forms/filtersets.py:977 dcim/forms/filtersets.py:1353 -#: dcim/forms/filtersets.py:1374 dcim/tables/devices.py:722 -#: dcim/tables/devices.py:782 dcim/tables/devices.py:1009 +#: dcim/forms/filtersets.py:1374 dcim/tables/devices.py:726 +#: dcim/tables/devices.py:786 dcim/tables/devices.py:1013 #: dcim/tables/devicetypes.py:245 dcim/tables/devicetypes.py:260 #: dcim/tables/racks.py:32 extras/forms/bulk_edit.py:259 #: extras/tables/tables.py:328 templates/circuits/circuittype.html:33 @@ -396,7 +402,7 @@ msgid "Color" msgstr "" #: circuits/forms/bulk_edit.py:113 circuits/forms/bulk_import.py:89 -#: circuits/forms/filtersets.py:126 core/forms/bulk_edit.py:17 +#: circuits/forms/filtersets.py:122 core/forms/bulk_edit.py:17 #: core/forms/filtersets.py:29 core/tables/data.py:20 core/tables/jobs.py:18 #: dcim/forms/bulk_edit.py:281 dcim/forms/bulk_edit.py:672 #: dcim/forms/bulk_edit.py:811 dcim/forms/bulk_edit.py:879 @@ -415,7 +421,7 @@ msgstr "" #: dcim/forms/filtersets.py:1253 dcim/forms/filtersets.py:1348 #: dcim/forms/filtersets.py:1369 dcim/forms/object_import.py:89 #: dcim/forms/object_import.py:118 dcim/forms/object_import.py:150 -#: dcim/tables/devices.py:211 dcim/tables/devices.py:838 +#: dcim/tables/devices.py:211 dcim/tables/devices.py:842 #: dcim/tables/power.py:77 extras/forms/bulk_import.py:39 #: extras/tables/tables.py:278 extras/tables/tables.py:350 #: extras/tables/tables.py:448 netbox/tables/tables.py:234 @@ -440,12 +446,12 @@ msgid "Type" msgstr "" #: circuits/forms/bulk_edit.py:123 circuits/forms/bulk_import.py:82 -#: circuits/forms/filtersets.py:139 circuits/forms/model_forms.py:97 +#: circuits/forms/filtersets.py:135 circuits/forms/model_forms.py:97 msgid "Provider account" msgstr "" #: circuits/forms/bulk_edit.py:131 circuits/forms/bulk_import.py:95 -#: circuits/forms/filtersets.py:150 core/forms/filtersets.py:34 +#: circuits/forms/filtersets.py:146 core/forms/filtersets.py:34 #: core/forms/filtersets.py:75 core/tables/data.py:23 core/tables/jobs.py:26 #: dcim/forms/bulk_edit.py:104 dcim/forms/bulk_edit.py:179 #: dcim/forms/bulk_edit.py:260 dcim/forms/bulk_edit.py:593 @@ -459,8 +465,8 @@ msgstr "" #: dcim/forms/filtersets.py:281 dcim/forms/filtersets.py:726 #: dcim/forms/filtersets.py:835 dcim/forms/filtersets.py:871 #: dcim/forms/filtersets.py:972 dcim/forms/filtersets.py:1083 -#: dcim/tables/devices.py:173 dcim/tables/devices.py:841 -#: dcim/tables/devices.py:1069 dcim/tables/modules.py:69 +#: dcim/tables/devices.py:173 dcim/tables/devices.py:845 +#: dcim/tables/devices.py:1073 dcim/tables/modules.py:69 #: dcim/tables/power.py:74 dcim/tables/racks.py:66 dcim/tables/sites.py:82 #: dcim/tables/sites.py:133 ipam/forms/bulk_edit.py:240 #: ipam/forms/bulk_edit.py:289 ipam/forms/bulk_edit.py:337 @@ -468,7 +474,7 @@ msgstr "" #: ipam/forms/bulk_import.py:256 ipam/forms/bulk_import.py:292 #: ipam/forms/bulk_import.py:458 ipam/forms/filtersets.py:205 #: ipam/forms/filtersets.py:270 ipam/forms/filtersets.py:341 -#: ipam/forms/filtersets.py:482 ipam/forms/model_forms.py:449 +#: ipam/forms/filtersets.py:482 ipam/forms/model_forms.py:435 #: ipam/tables/ip.py:236 ipam/tables/ip.py:309 ipam/tables/ip.py:359 #: ipam/tables/ip.py:421 ipam/tables/ip.py:448 ipam/tables/vlans.py:122 #: ipam/tables/vlans.py:227 templates/circuits/circuit.html:35 @@ -487,7 +493,7 @@ msgstr "" #: virtualization/forms/bulk_edit.py:117 virtualization/forms/bulk_import.py:54 #: virtualization/forms/bulk_import.py:80 virtualization/forms/filtersets.py:61 #: virtualization/forms/filtersets.py:156 virtualization/tables/clusters.py:74 -#: virtualization/tables/virtualmachines.py:50 vpn/forms/bulk_edit.py:38 +#: virtualization/tables/virtualmachines.py:59 vpn/forms/bulk_edit.py:38 #: vpn/forms/bulk_import.py:37 vpn/forms/filtersets.py:46 #: vpn/tables/tunnels.py:48 wireless/forms/bulk_edit.py:42 #: wireless/forms/bulk_edit.py:104 wireless/forms/bulk_import.py:43 @@ -498,7 +504,7 @@ msgid "Status" msgstr "" #: circuits/forms/bulk_edit.py:137 circuits/forms/bulk_import.py:100 -#: circuits/forms/filtersets.py:119 dcim/forms/bulk_edit.py:120 +#: circuits/forms/filtersets.py:115 dcim/forms/bulk_edit.py:120 #: dcim/forms/bulk_edit.py:185 dcim/forms/bulk_edit.py:255 #: dcim/forms/bulk_edit.py:366 dcim/forms/bulk_edit.py:583 #: dcim/forms/bulk_edit.py:684 dcim/forms/bulk_edit.py:1590 @@ -554,15 +560,15 @@ msgstr "" msgid "Tenant" msgstr "" -#: circuits/forms/bulk_edit.py:142 circuits/forms/filtersets.py:174 +#: circuits/forms/bulk_edit.py:142 circuits/forms/filtersets.py:170 msgid "Install date" msgstr "" -#: circuits/forms/bulk_edit.py:147 circuits/forms/filtersets.py:179 +#: circuits/forms/bulk_edit.py:147 circuits/forms/filtersets.py:175 msgid "Termination date" msgstr "" -#: circuits/forms/bulk_edit.py:153 circuits/forms/filtersets.py:186 +#: circuits/forms/bulk_edit.py:153 circuits/forms/filtersets.py:182 msgid "Commit rate (Kbps)" msgstr "" @@ -595,7 +601,7 @@ msgstr "" #: circuits/forms/bulk_import.py:70 dcim/forms/bulk_import.py:178 #: dcim/forms/bulk_import.py:388 dcim/forms/bulk_import.py:1108 -#: dcim/forms/bulk_import.py:1187 extras/forms/bulk_import.py:229 +#: dcim/forms/bulk_import.py:1187 extras/forms/bulk_import.py:235 msgid "RGB color in hexadecimal. Example:" msgstr "" @@ -631,12 +637,12 @@ msgstr "" msgid "Assigned tenant" msgstr "" -#: circuits/forms/bulk_import.py:123 circuits/forms/filtersets.py:147 +#: circuits/forms/bulk_import.py:123 circuits/forms/filtersets.py:143 #: circuits/forms/model_forms.py:143 msgid "Provider network" msgstr "" -#: circuits/forms/filtersets.py:26 circuits/forms/filtersets.py:118 +#: circuits/forms/filtersets.py:26 circuits/forms/filtersets.py:114 #: dcim/forms/bulk_edit.py:247 dcim/forms/bulk_edit.py:345 #: dcim/forms/bulk_edit.py:575 dcim/forms/bulk_edit.py:622 #: dcim/forms/bulk_edit.py:772 dcim/forms/bulk_import.py:189 @@ -660,7 +666,7 @@ msgstr "" #: extras/filtersets.py:441 extras/forms/filtersets.py:328 #: ipam/forms/bulk_edit.py:456 ipam/forms/filtersets.py:168 #: ipam/forms/filtersets.py:400 ipam/forms/filtersets.py:422 -#: ipam/forms/filtersets.py:448 ipam/forms/model_forms.py:560 +#: ipam/forms/filtersets.py:448 ipam/forms/model_forms.py:564 #: templates/dcim/device.html:26 templates/dcim/device_edit.html:30 #: templates/dcim/inc/cable_termination.html:12 templates/dcim/location.html:27 #: templates/dcim/powerpanel.html:27 templates/dcim/rack.html:29 @@ -670,13 +676,7 @@ msgstr "" msgid "Location" msgstr "" -#: circuits/forms/filtersets.py:27 ipam/forms/model_forms.py:158 -#: ipam/models/asns.py:108 ipam/models/asns.py:125 ipam/tables/asn.py:41 -#: templates/ipam/asn.html:20 -msgid "ASN" -msgstr "" - -#: circuits/forms/filtersets.py:28 circuits/forms/filtersets.py:120 +#: circuits/forms/filtersets.py:28 circuits/forms/filtersets.py:116 #: dcim/forms/filtersets.py:136 dcim/forms/filtersets.py:150 #: dcim/forms/filtersets.py:166 dcim/forms/filtersets.py:198 #: dcim/forms/filtersets.py:249 dcim/forms/filtersets.py:334 @@ -689,7 +689,7 @@ msgstr "" msgid "Contacts" msgstr "" -#: circuits/forms/filtersets.py:33 circuits/forms/filtersets.py:157 +#: circuits/forms/filtersets.py:33 circuits/forms/filtersets.py:153 #: dcim/forms/bulk_edit.py:110 dcim/forms/bulk_edit.py:222 #: dcim/forms/bulk_edit.py:747 dcim/forms/bulk_import.py:92 #: dcim/forms/filtersets.py:70 dcim/forms/filtersets.py:177 @@ -704,7 +704,7 @@ msgstr "" #: ipam/forms/bulk_edit.py:205 ipam/forms/bulk_edit.py:437 #: ipam/forms/bulk_edit.py:509 ipam/forms/filtersets.py:212 #: ipam/forms/filtersets.py:407 ipam/forms/filtersets.py:456 -#: ipam/forms/model_forms.py:532 templates/dcim/device.html:18 +#: ipam/forms/model_forms.py:536 templates/dcim/device.html:18 #: templates/dcim/rack.html:19 templates/dcim/rackreservation.html:25 #: templates/dcim/region.html:26 templates/dcim/site.html:31 #: templates/ipam/prefix.html:50 templates/ipam/vlan.html:19 @@ -714,7 +714,7 @@ msgstr "" msgid "Region" msgstr "" -#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:162 +#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:158 #: dcim/forms/bulk_edit.py:230 dcim/forms/bulk_edit.py:755 #: dcim/forms/filtersets.py:75 dcim/forms/filtersets.py:182 #: dcim/forms/filtersets.py:208 dcim/forms/filtersets.py:269 @@ -724,19 +724,15 @@ msgstr "" #: extras/filtersets.py:425 ipam/forms/bulk_edit.py:210 #: ipam/forms/bulk_edit.py:444 ipam/forms/bulk_edit.py:514 #: ipam/forms/filtersets.py:217 ipam/forms/filtersets.py:412 -#: ipam/forms/filtersets.py:461 ipam/forms/model_forms.py:545 +#: ipam/forms/filtersets.py:461 ipam/forms/model_forms.py:549 #: virtualization/forms/bulk_edit.py:85 virtualization/forms/filtersets.py:68 #: virtualization/forms/filtersets.py:134 #: virtualization/forms/model_forms.py:101 msgid "Site group" msgstr "" -#: circuits/forms/filtersets.py:51 -msgid "ASN (legacy)" -msgstr "" - -#: circuits/forms/filtersets.py:65 circuits/forms/filtersets.py:83 -#: circuits/forms/filtersets.py:102 circuits/forms/filtersets.py:117 +#: circuits/forms/filtersets.py:61 circuits/forms/filtersets.py:79 +#: circuits/forms/filtersets.py:98 circuits/forms/filtersets.py:113 #: core/forms/filtersets.py:63 dcim/forms/bulk_edit.py:718 #: dcim/forms/filtersets.py:164 dcim/forms/filtersets.py:196 #: dcim/forms/filtersets.py:825 dcim/forms/filtersets.py:920 @@ -761,7 +757,7 @@ msgstr "" msgid "Attributes" msgstr "" -#: circuits/forms/filtersets.py:73 circuits/tables/circuits.py:60 +#: circuits/forms/filtersets.py:69 circuits/tables/circuits.py:60 #: circuits/tables/providers.py:66 templates/circuits/circuit.html:23 #: templates/circuits/provideraccount.html:25 msgid "Account" @@ -782,7 +778,7 @@ msgstr "" #: dcim/models/device_component_templates.py:491 #: dcim/models/device_component_templates.py:591 #: dcim/models/device_components.py:976 dcim/models/device_components.py:1050 -#: dcim/models/device_components.py:1166 dcim/models/devices.py:467 +#: dcim/models/device_components.py:1166 dcim/models/devices.py:469 #: dcim/models/racks.py:43 extras/models/tags.py:28 msgid "color" msgstr "" @@ -804,8 +800,8 @@ msgid "Unique circuit ID" msgstr "" #: circuits/models/circuits.py:67 core/models/data.py:55 core/models/jobs.py:85 -#: dcim/models/cables.py:49 dcim/models/devices.py:641 -#: dcim/models/devices.py:1165 dcim/models/devices.py:1374 +#: dcim/models/cables.py:49 dcim/models/devices.py:643 +#: dcim/models/devices.py:1170 dcim/models/devices.py:1379 #: dcim/models/power.py:95 dcim/models/racks.py:97 dcim/models/sites.py:154 #: dcim/models/sites.py:266 ipam/models/ip.py:252 ipam/models/ip.py:521 #: ipam/models/ip.py:729 ipam/models/vlans.py:175 @@ -883,7 +879,7 @@ msgstr "" #: extras/models/models.py:541 extras/models/staging.py:31 #: extras/models/tags.py:32 netbox/models/__init__.py:109 #: netbox/models/__init__.py:144 netbox/models/__init__.py:190 -#: users/models.py:273 users/models.py:348 +#: users/models.py:274 users/models.py:353 #: virtualization/models/virtualmachines.py:282 msgid "description" msgstr "" @@ -909,8 +905,8 @@ msgstr "" #: circuits/models/providers.py:22 circuits/models/providers.py:66 #: circuits/models/providers.py:104 core/models/data.py:42 #: core/models/jobs.py:46 dcim/models/device_component_templates.py:43 -#: dcim/models/device_components.py:54 dcim/models/devices.py:581 -#: dcim/models/devices.py:1305 dcim/models/devices.py:1370 +#: dcim/models/device_components.py:54 dcim/models/devices.py:583 +#: dcim/models/devices.py:1310 dcim/models/devices.py:1375 #: dcim/models/power.py:39 dcim/models/power.py:91 dcim/models/racks.py:62 #: dcim/models/sites.py:138 extras/models/configs.py:36 #: extras/models/configs.py:215 extras/models/customfields.py:89 @@ -923,7 +919,7 @@ msgstr "" #: ipam/models/vrfs.py:79 netbox/models/__init__.py:136 #: netbox/models/__init__.py:180 tenancy/models/contacts.py:64 #: tenancy/models/tenants.py:20 tenancy/models/tenants.py:45 -#: users/models.py:344 virtualization/models/clusters.py:57 +#: users/models.py:349 virtualization/models/clusters.py:57 #: virtualization/models/virtualmachines.py:70 #: virtualization/models/virtualmachines.py:272 vpn/models/crypto.py:24 #: vpn/models/crypto.py:71 vpn/models/crypto.py:131 vpn/models/crypto.py:183 @@ -981,13 +977,13 @@ msgstr "" #: core/tables/data.py:16 core/tables/jobs.py:14 dcim/forms/filtersets.py:60 #: dcim/forms/object_create.py:42 dcim/tables/devices.py:88 #: dcim/tables/devices.py:125 dcim/tables/devices.py:167 -#: dcim/tables/devices.py:318 dcim/tables/devices.py:400 -#: dcim/tables/devices.py:444 dcim/tables/devices.py:496 -#: dcim/tables/devices.py:548 dcim/tables/devices.py:668 -#: dcim/tables/devices.py:749 dcim/tables/devices.py:799 -#: dcim/tables/devices.py:865 dcim/tables/devices.py:980 -#: dcim/tables/devices.py:1000 dcim/tables/devices.py:1029 -#: dcim/tables/devices.py:1059 dcim/tables/devicetypes.py:32 +#: dcim/tables/devices.py:322 dcim/tables/devices.py:404 +#: dcim/tables/devices.py:448 dcim/tables/devices.py:500 +#: dcim/tables/devices.py:552 dcim/tables/devices.py:672 +#: dcim/tables/devices.py:753 dcim/tables/devices.py:803 +#: dcim/tables/devices.py:869 dcim/tables/devices.py:984 +#: dcim/tables/devices.py:1004 dcim/tables/devices.py:1033 +#: dcim/tables/devices.py:1063 dcim/tables/devicetypes.py:32 #: dcim/tables/power.py:22 dcim/tables/power.py:62 dcim/tables/racks.py:23 #: dcim/tables/racks.py:53 dcim/tables/sites.py:24 dcim/tables/sites.py:51 #: dcim/tables/sites.py:78 dcim/tables/sites.py:125 @@ -1050,9 +1046,9 @@ msgstr "" #: virtualization/forms/object_create.py:23 #: virtualization/tables/clusters.py:17 virtualization/tables/clusters.py:39 #: virtualization/tables/clusters.py:62 -#: virtualization/tables/virtualmachines.py:45 -#: virtualization/tables/virtualmachines.py:119 -#: virtualization/tables/virtualmachines.py:172 vpn/tables/crypto.py:18 +#: virtualization/tables/virtualmachines.py:54 +#: virtualization/tables/virtualmachines.py:132 +#: virtualization/tables/virtualmachines.py:185 vpn/tables/crypto.py:18 #: vpn/tables/crypto.py:57 vpn/tables/crypto.py:93 vpn/tables/crypto.py:129 #: vpn/tables/crypto.py:158 vpn/tables/l2vpn.py:23 vpn/tables/tunnels.py:18 #: vpn/tables/tunnels.py:40 wireless/tables/wirelesslan.py:18 @@ -1087,7 +1083,7 @@ msgstr "" #: circuits/tables/circuits.py:75 circuits/tables/providers.py:48 #: circuits/tables/providers.py:82 circuits/tables/providers.py:107 -#: dcim/tables/devices.py:1042 dcim/tables/devicetypes.py:92 +#: dcim/tables/devices.py:1046 dcim/tables/devicetypes.py:92 #: dcim/tables/modules.py:29 dcim/tables/modules.py:72 dcim/tables/power.py:39 #: dcim/tables/power.py:96 dcim/tables/racks.py:76 dcim/tables/racks.py:156 #: dcim/tables/sites.py:103 extras/forms/bulk_edit.py:320 @@ -1099,7 +1095,7 @@ msgstr "" #: templates/inc/panels/comments.html:6 tenancy/tables/contacts.py:68 #: tenancy/tables/tenants.py:46 utilities/forms/fields/fields.py:29 #: virtualization/tables/clusters.py:91 -#: virtualization/tables/virtualmachines.py:68 vpn/tables/crypto.py:37 +#: virtualization/tables/virtualmachines.py:81 vpn/tables/crypto.py:37 #: vpn/tables/crypto.py:74 vpn/tables/crypto.py:109 vpn/tables/crypto.py:140 #: vpn/tables/crypto.py:173 vpn/tables/l2vpn.py:37 vpn/tables/tunnels.py:61 #: wireless/tables/wirelesslan.py:27 wireless/tables/wirelesslan.py:58 @@ -1136,7 +1132,7 @@ msgid "Completed" msgstr "" #: core/choices.py:22 core/choices.py:59 dcim/choices.py:176 -#: dcim/choices.py:222 dcim/choices.py:1496 extras/choices.py:212 +#: dcim/choices.py:222 dcim/choices.py:1502 extras/choices.py:212 #: virtualization/choices.py:47 msgid "Failed" msgstr "" @@ -1218,7 +1214,7 @@ msgstr "" #: core/forms/bulk_edit.py:24 core/forms/filtersets.py:39 #: core/tables/data.py:26 dcim/forms/bulk_edit.py:1012 #: dcim/forms/bulk_edit.py:1285 dcim/forms/filtersets.py:1270 -#: dcim/tables/devices.py:573 dcim/tables/devicetypes.py:221 +#: dcim/tables/devices.py:577 dcim/tables/devicetypes.py:221 #: extras/forms/bulk_edit.py:97 extras/forms/bulk_edit.py:161 #: extras/forms/bulk_edit.py:220 extras/forms/filtersets.py:119 #: extras/forms/filtersets.py:206 extras/forms/filtersets.py:267 @@ -1352,7 +1348,7 @@ msgstr "" msgid "Rack Elevations" msgstr "" -#: core/forms/model_forms.py:148 dcim/choices.py:1407 +#: core/forms/model_forms.py:148 dcim/choices.py:1413 #: dcim/forms/bulk_edit.py:859 dcim/forms/bulk_edit.py:1242 #: dcim/forms/bulk_edit.py:1260 dcim/tables/racks.py:89 #: netbox/navigation/menu.py:276 netbox/navigation/menu.py:280 @@ -1413,7 +1409,7 @@ msgstr "" #: core/models/config.py:18 core/models/data.py:282 core/models/files.py:27 #: core/models/jobs.py:50 extras/models/models.py:760 -#: netbox/models/features.py:52 users/models.py:248 +#: netbox/models/features.py:52 users/models.py:249 msgid "created" msgstr "" @@ -1471,7 +1467,7 @@ msgstr "" #: core/models/data.py:62 dcim/models/device_component_templates.py:392 #: dcim/models/device_components.py:513 extras/models/models.py:88 -#: extras/models/models.py:331 extras/models/models.py:556 users/models.py:353 +#: extras/models/models.py:331 extras/models/models.py:556 users/models.py:358 msgid "enabled" msgstr "" @@ -1681,7 +1677,7 @@ msgid "Staging" msgstr "" #: dcim/choices.py:23 dcim/choices.py:178 dcim/choices.py:223 -#: dcim/choices.py:1420 virtualization/choices.py:23 +#: dcim/choices.py:1426 virtualization/choices.py:23 #: virtualization/choices.py:48 msgid "Decommissioning" msgstr "" @@ -1741,7 +1737,7 @@ msgstr "" msgid "Millimeters" msgstr "" -#: dcim/choices.py:115 dcim/choices.py:1442 +#: dcim/choices.py:115 dcim/choices.py:1448 msgid "Inches" msgstr "" @@ -1753,8 +1749,8 @@ msgstr "" #: dcim/forms/filtersets.py:226 dcim/forms/model_forms.py:73 #: dcim/forms/model_forms.py:94 dcim/forms/model_forms.py:172 #: dcim/forms/model_forms.py:962 dcim/forms/model_forms.py:1303 -#: dcim/forms/object_import.py:181 dcim/tables/devices.py:676 -#: dcim/tables/devices.py:960 extras/tables/tables.py:181 +#: dcim/forms/object_import.py:181 dcim/tables/devices.py:680 +#: dcim/tables/devices.py:964 extras/tables/tables.py:181 #: ipam/tables/fhrp.py:59 ipam/tables/ip.py:374 ipam/tables/services.py:44 #: templates/dcim/interface.html:105 templates/dcim/interface.html:321 #: templates/dcim/location.html:44 templates/dcim/region.html:38 @@ -1766,7 +1762,7 @@ msgstr "" #: tenancy/forms/bulk_import.py:58 tenancy/forms/model_forms.py:24 #: tenancy/forms/model_forms.py:69 virtualization/forms/bulk_edit.py:206 #: virtualization/forms/bulk_import.py:151 -#: virtualization/tables/virtualmachines.py:142 wireless/forms/bulk_edit.py:23 +#: virtualization/tables/virtualmachines.py:155 wireless/forms/bulk_edit.py:23 #: wireless/forms/bulk_import.py:21 wireless/forms/model_forms.py:20 msgid "Parent" msgstr "" @@ -1815,7 +1811,7 @@ msgstr "" msgid "Side to rear" msgstr "" -#: dcim/choices.py:198 dcim/choices.py:1215 +#: dcim/choices.py:198 dcim/choices.py:1221 msgid "Passive" msgstr "" @@ -1843,8 +1839,8 @@ msgstr "" msgid "Proprietary" msgstr "" -#: dcim/choices.py:534 dcim/choices.py:764 dcim/choices.py:1131 -#: dcim/choices.py:1133 dcim/choices.py:1338 dcim/choices.py:1340 +#: dcim/choices.py:534 dcim/choices.py:764 dcim/choices.py:1137 +#: dcim/choices.py:1139 dcim/choices.py:1344 dcim/choices.py:1346 #: netbox/navigation/menu.py:188 msgid "Other" msgstr "" @@ -1857,177 +1853,177 @@ msgstr "" msgid "Physical" msgstr "" -#: dcim/choices.py:795 dcim/choices.py:949 +#: dcim/choices.py:795 dcim/choices.py:952 msgid "Virtual" msgstr "" -#: dcim/choices.py:796 dcim/choices.py:1019 dcim/forms/bulk_edit.py:1398 +#: dcim/choices.py:796 dcim/choices.py:1022 dcim/forms/bulk_edit.py:1398 #: dcim/forms/filtersets.py:1233 dcim/forms/model_forms.py:888 #: dcim/forms/model_forms.py:1197 netbox/navigation/menu.py:128 #: netbox/navigation/menu.py:132 templates/dcim/interface.html:217 msgid "Wireless" msgstr "" -#: dcim/choices.py:947 +#: dcim/choices.py:950 msgid "Virtual interfaces" msgstr "" -#: dcim/choices.py:950 dcim/forms/bulk_edit.py:1295 +#: dcim/choices.py:953 dcim/forms/bulk_edit.py:1295 #: dcim/forms/bulk_import.py:785 dcim/forms/model_forms.py:876 -#: dcim/tables/devices.py:680 templates/dcim/interface.html:109 +#: dcim/tables/devices.py:684 templates/dcim/interface.html:109 #: templates/virtualization/vminterface.html:46 #: virtualization/forms/bulk_edit.py:211 #: virtualization/forms/bulk_import.py:158 -#: virtualization/tables/virtualmachines.py:146 +#: virtualization/tables/virtualmachines.py:159 msgid "Bridge" msgstr "" -#: dcim/choices.py:951 +#: dcim/choices.py:954 msgid "Link Aggregation Group (LAG)" msgstr "" -#: dcim/choices.py:955 +#: dcim/choices.py:958 msgid "Ethernet (fixed)" msgstr "" -#: dcim/choices.py:969 +#: dcim/choices.py:972 msgid "Ethernet (modular)" msgstr "" -#: dcim/choices.py:1005 +#: dcim/choices.py:1008 msgid "Ethernet (backplane)" msgstr "" -#: dcim/choices.py:1033 +#: dcim/choices.py:1036 msgid "Cellular" msgstr "" -#: dcim/choices.py:1080 dcim/forms/filtersets.py:302 +#: dcim/choices.py:1086 dcim/forms/filtersets.py:302 #: dcim/forms/filtersets.py:736 dcim/forms/filtersets.py:876 #: dcim/forms/filtersets.py:1426 templates/dcim/inventoryitem.html:53 #: templates/dcim/virtualchassis_edit.html:55 msgid "Serial" msgstr "" -#: dcim/choices.py:1095 +#: dcim/choices.py:1101 msgid "Coaxial" msgstr "" -#: dcim/choices.py:1112 +#: dcim/choices.py:1118 msgid "Stacking" msgstr "" -#: dcim/choices.py:1162 +#: dcim/choices.py:1168 msgid "Half" msgstr "" -#: dcim/choices.py:1163 +#: dcim/choices.py:1169 msgid "Full" msgstr "" -#: dcim/choices.py:1164 netbox/preferences.py:29 wireless/choices.py:480 +#: dcim/choices.py:1170 netbox/preferences.py:29 wireless/choices.py:480 msgid "Auto" msgstr "" -#: dcim/choices.py:1175 +#: dcim/choices.py:1181 msgid "Access" msgstr "" -#: dcim/choices.py:1176 ipam/tables/vlans.py:168 ipam/tables/vlans.py:213 +#: dcim/choices.py:1182 ipam/tables/vlans.py:168 ipam/tables/vlans.py:213 #: templates/dcim/inc/interface_vlans_table.html:7 msgid "Tagged" msgstr "" -#: dcim/choices.py:1177 +#: dcim/choices.py:1183 msgid "Tagged (All)" msgstr "" -#: dcim/choices.py:1206 +#: dcim/choices.py:1212 msgid "IEEE Standard" msgstr "" -#: dcim/choices.py:1217 +#: dcim/choices.py:1223 msgid "Passive 24V (2-pair)" msgstr "" -#: dcim/choices.py:1218 +#: dcim/choices.py:1224 msgid "Passive 24V (4-pair)" msgstr "" -#: dcim/choices.py:1219 +#: dcim/choices.py:1225 msgid "Passive 48V (2-pair)" msgstr "" -#: dcim/choices.py:1220 +#: dcim/choices.py:1226 msgid "Passive 48V (4-pair)" msgstr "" -#: dcim/choices.py:1282 dcim/choices.py:1378 +#: dcim/choices.py:1288 dcim/choices.py:1384 msgid "Copper" msgstr "" -#: dcim/choices.py:1305 +#: dcim/choices.py:1311 msgid "Fiber Optic" msgstr "" -#: dcim/choices.py:1394 +#: dcim/choices.py:1400 msgid "Fiber" msgstr "" -#: dcim/choices.py:1418 dcim/forms/filtersets.py:1140 +#: dcim/choices.py:1424 dcim/forms/filtersets.py:1140 msgid "Connected" msgstr "" -#: dcim/choices.py:1437 +#: dcim/choices.py:1443 msgid "Kilometers" msgstr "" -#: dcim/choices.py:1438 templates/dcim/cable_trace.html:62 +#: dcim/choices.py:1444 templates/dcim/cable_trace.html:62 msgid "Meters" msgstr "" -#: dcim/choices.py:1439 +#: dcim/choices.py:1445 msgid "Centimeters" msgstr "" -#: dcim/choices.py:1440 +#: dcim/choices.py:1446 msgid "Miles" msgstr "" -#: dcim/choices.py:1441 templates/dcim/cable_trace.html:63 +#: dcim/choices.py:1447 templates/dcim/cable_trace.html:63 msgid "Feet" msgstr "" -#: dcim/choices.py:1457 templates/dcim/device.html:332 +#: dcim/choices.py:1463 templates/dcim/device.html:332 #: templates/dcim/rack.html:157 msgid "Kilograms" msgstr "" -#: dcim/choices.py:1458 +#: dcim/choices.py:1464 msgid "Grams" msgstr "" -#: dcim/choices.py:1459 templates/dcim/rack.html:158 +#: dcim/choices.py:1465 templates/dcim/rack.html:158 msgid "Pounds" msgstr "" -#: dcim/choices.py:1460 +#: dcim/choices.py:1466 msgid "Ounces" msgstr "" -#: dcim/choices.py:1506 tenancy/choices.py:17 +#: dcim/choices.py:1512 tenancy/choices.py:17 msgid "Primary" msgstr "" -#: dcim/choices.py:1507 +#: dcim/choices.py:1513 msgid "Redundant" msgstr "" -#: dcim/choices.py:1528 +#: dcim/choices.py:1534 msgid "Single phase" msgstr "" -#: dcim/choices.py:1529 +#: dcim/choices.py:1535 msgid "Three-phase" msgstr "" @@ -2304,7 +2300,7 @@ msgid "Virtual Chassis (ID)" msgstr "" #: dcim/filtersets.py:1303 dcim/forms/filtersets.py:106 -#: dcim/tables/devices.py:235 netbox/navigation/menu.py:67 +#: dcim/tables/devices.py:239 netbox/navigation/menu.py:67 #: templates/dcim/device.html:123 templates/dcim/device_edit.html:93 #: templates/dcim/virtualchassis.html:20 #: templates/dcim/virtualchassis_add.html:8 @@ -2328,7 +2324,7 @@ msgstr "" #: dcim/filtersets.py:1448 dcim/forms/bulk_edit.py:1374 #: dcim/forms/bulk_import.py:836 dcim/forms/filtersets.py:1328 #: dcim/forms/model_forms.py:1182 dcim/models/device_components.py:712 -#: dcim/tables/devices.py:642 ipam/filtersets.py:282 ipam/filtersets.py:293 +#: dcim/tables/devices.py:646 ipam/filtersets.py:282 ipam/filtersets.py:293 #: ipam/filtersets.py:449 ipam/filtersets.py:550 ipam/filtersets.py:561 #: ipam/forms/bulk_edit.py:226 ipam/forms/bulk_edit.py:281 #: ipam/forms/bulk_edit.py:323 ipam/forms/bulk_import.py:156 @@ -2336,8 +2332,8 @@ msgstr "" #: ipam/forms/filtersets.py:66 ipam/forms/filtersets.py:167 #: ipam/forms/filtersets.py:295 ipam/forms/model_forms.py:59 #: ipam/forms/model_forms.py:203 ipam/forms/model_forms.py:246 -#: ipam/forms/model_forms.py:290 ipam/forms/model_forms.py:412 -#: ipam/forms/model_forms.py:426 ipam/forms/model_forms.py:440 +#: ipam/forms/model_forms.py:290 ipam/forms/model_forms.py:398 +#: ipam/forms/model_forms.py:412 ipam/forms/model_forms.py:426 #: ipam/models/ip.py:232 ipam/models/ip.py:511 ipam/models/ip.py:719 #: ipam/models/vrfs.py:62 ipam/tables/ip.py:241 ipam/tables/ip.py:306 #: ipam/tables/ip.py:356 ipam/tables/ip.py:445 @@ -2350,7 +2346,7 @@ msgstr "" #: virtualization/forms/filtersets.py:220 #: virtualization/forms/model_forms.py:347 #: virtualization/models/virtualmachines.py:348 -#: virtualization/tables/virtualmachines.py:123 +#: virtualization/tables/virtualmachines.py:136 msgid "VRF" msgstr "" @@ -2364,7 +2360,7 @@ msgid "L2VPN (ID)" msgstr "" #: dcim/filtersets.py:1465 dcim/forms/filtersets.py:1333 -#: dcim/tables/devices.py:590 ipam/filtersets.py:973 +#: dcim/tables/devices.py:594 ipam/filtersets.py:973 #: ipam/forms/filtersets.py:499 ipam/tables/vlans.py:133 #: templates/dcim/interface.html:94 templates/ipam/vlan.html:69 #: templates/vpn/l2vpntermination.html:15 @@ -2435,7 +2431,7 @@ msgstr "" #: dcim/forms/bulk_create.py:112 dcim/forms/filtersets.py:1390 #: dcim/forms/model_forms.py:426 dcim/forms/model_forms.py:475 #: dcim/forms/object_create.py:196 dcim/forms/object_create.py:352 -#: dcim/tables/devices.py:198 dcim/tables/devices.py:725 +#: dcim/tables/devices.py:198 dcim/tables/devices.py:729 #: dcim/tables/devicetypes.py:242 templates/dcim/device.html:45 #: templates/dcim/device.html:129 templates/dcim/modulebay.html:35 #: templates/dcim/virtualchassis.html:59 @@ -2452,7 +2448,7 @@ msgstr "" #: dcim/forms/bulk_edit.py:115 dcim/forms/bulk_import.py:99 #: dcim/forms/model_forms.py:120 dcim/tables/sites.py:89 ipam/filtersets.py:936 #: ipam/forms/bulk_edit.py:528 ipam/forms/bulk_import.py:444 -#: ipam/forms/model_forms.py:509 ipam/tables/fhrp.py:67 +#: ipam/forms/model_forms.py:495 ipam/tables/fhrp.py:67 #: ipam/tables/vlans.py:118 ipam/tables/vlans.py:221 #: templates/dcim/interface.html:294 templates/dcim/site.html:37 #: templates/ipam/inc/panels/fhrp_groups.html:10 templates/ipam/vlan.html:30 @@ -2502,8 +2498,8 @@ msgstr "" #: dcim/forms/filtersets.py:704 dcim/forms/filtersets.py:1417 #: dcim/forms/model_forms.py:224 dcim/forms/model_forms.py:970 #: dcim/forms/model_forms.py:1311 dcim/forms/object_import.py:186 -#: dcim/tables/devices.py:202 dcim/tables/devices.py:833 -#: dcim/tables/devices.py:944 dcim/tables/devicetypes.py:300 +#: dcim/tables/devices.py:202 dcim/tables/devices.py:837 +#: dcim/tables/devices.py:948 dcim/tables/devicetypes.py:300 #: dcim/tables/racks.py:69 extras/filtersets.py:457 ipam/forms/bulk_edit.py:245 #: ipam/forms/bulk_edit.py:294 ipam/forms/bulk_edit.py:342 #: ipam/forms/bulk_edit.py:546 ipam/forms/bulk_import.py:196 @@ -2512,7 +2508,7 @@ msgstr "" #: ipam/forms/filtersets.py:278 ipam/forms/filtersets.py:346 #: ipam/forms/filtersets.py:490 ipam/forms/model_forms.py:187 #: ipam/forms/model_forms.py:222 ipam/forms/model_forms.py:249 -#: ipam/forms/model_forms.py:647 ipam/tables/ip.py:257 ipam/tables/ip.py:313 +#: ipam/forms/model_forms.py:651 ipam/tables/ip.py:257 ipam/tables/ip.py:313 #: ipam/tables/ip.py:363 ipam/tables/vlans.py:126 ipam/tables/vlans.py:230 #: templates/dcim/device.html:187 #: templates/dcim/inc/panels/inventory_items.html:12 @@ -2529,7 +2525,7 @@ msgstr "" #: virtualization/forms/bulk_import.py:106 #: virtualization/forms/filtersets.py:153 #: virtualization/forms/model_forms.py:198 -#: virtualization/tables/virtualmachines.py:65 vpn/forms/bulk_edit.py:86 +#: virtualization/tables/virtualmachines.py:74 vpn/forms/bulk_edit.py:86 #: vpn/forms/bulk_import.py:81 vpn/forms/filtersets.py:84 #: vpn/forms/model_forms.py:77 vpn/forms/model_forms.py:112 #: vpn/tables/tunnels.py:82 @@ -2623,7 +2619,7 @@ msgstr "" #: dcim/forms/model_forms.py:669 dcim/forms/object_create.py:399 #: dcim/tables/devices.py:194 dcim/tables/power.py:70 dcim/tables/racks.py:148 #: ipam/forms/bulk_edit.py:464 ipam/forms/filtersets.py:427 -#: ipam/forms/model_forms.py:571 templates/dcim/device.html:30 +#: ipam/forms/model_forms.py:575 templates/dcim/device.html:30 #: templates/dcim/inc/cable_termination.html:16 #: templates/dcim/powerfeed.html:31 templates/dcim/rack.html:14 #: templates/dcim/rack/base.html:4 templates/dcim/rack_edit.html:8 @@ -2655,7 +2651,7 @@ msgstr "" #: dcim/forms/model_forms.py:334 dcim/forms/model_forms.py:374 #: dcim/forms/model_forms.py:975 dcim/forms/model_forms.py:1316 #: dcim/forms/object_import.py:192 dcim/tables/devices.py:129 -#: dcim/tables/devices.py:205 dcim/tables/devices.py:947 +#: dcim/tables/devices.py:205 dcim/tables/devices.py:951 #: dcim/tables/devicetypes.py:81 dcim/tables/devicetypes.py:304 #: dcim/tables/modules.py:20 dcim/tables/modules.py:60 #: templates/dcim/devicetype.html:17 templates/dcim/inventoryitem.html:45 @@ -2702,7 +2698,7 @@ msgstr "" msgid "Module Type" msgstr "" -#: dcim/forms/bulk_edit.py:506 dcim/models/devices.py:472 +#: dcim/forms/bulk_edit.py:506 dcim/models/devices.py:474 msgid "VM role" msgstr "" @@ -2734,13 +2730,15 @@ msgstr "" #: dcim/forms/bulk_edit.py:588 dcim/forms/bulk_import.py:443 #: dcim/forms/filtersets.py:723 dcim/forms/model_forms.py:389 -#: dcim/forms/model_forms.py:448 extras/filtersets.py:468 -#: templates/dcim/device.html:191 templates/dcim/platform.html:27 +#: dcim/forms/model_forms.py:448 dcim/tables/devices.py:215 +#: extras/filtersets.py:468 templates/dcim/device.html:191 +#: templates/dcim/platform.html:27 #: templates/virtualization/virtualmachine.html:30 #: virtualization/forms/bulk_edit.py:159 #: virtualization/forms/bulk_import.py:122 #: virtualization/forms/filtersets.py:164 #: virtualization/forms/model_forms.py:206 +#: virtualization/tables/virtualmachines.py:78 msgid "Platform" msgstr "" @@ -2764,16 +2762,16 @@ msgstr "" #: dcim/forms/model_forms.py:760 dcim/forms/model_forms.py:1011 #: dcim/forms/model_forms.py:1460 dcim/forms/object_create.py:256 #: dcim/tables/connections.py:22 dcim/tables/connections.py:41 -#: dcim/tables/connections.py:60 dcim/tables/devices.py:314 -#: dcim/tables/devices.py:379 dcim/tables/devices.py:423 -#: dcim/tables/devices.py:468 dcim/tables/devices.py:522 -#: dcim/tables/devices.py:614 dcim/tables/devices.py:715 -#: dcim/tables/devices.py:775 dcim/tables/devices.py:825 -#: dcim/tables/devices.py:885 dcim/tables/devices.py:937 -#: dcim/tables/devices.py:1063 dcim/tables/modules.py:52 +#: dcim/tables/connections.py:60 dcim/tables/devices.py:318 +#: dcim/tables/devices.py:383 dcim/tables/devices.py:427 +#: dcim/tables/devices.py:472 dcim/tables/devices.py:526 +#: dcim/tables/devices.py:618 dcim/tables/devices.py:719 +#: dcim/tables/devices.py:779 dcim/tables/devices.py:829 +#: dcim/tables/devices.py:889 dcim/tables/devices.py:941 +#: dcim/tables/devices.py:1067 dcim/tables/modules.py:52 #: extras/forms/filtersets.py:329 ipam/forms/bulk_import.py:303 #: ipam/forms/bulk_import.py:489 ipam/forms/filtersets.py:532 -#: ipam/forms/model_forms.py:685 ipam/tables/vlans.py:176 +#: ipam/forms/model_forms.py:689 ipam/tables/vlans.py:176 #: templates/dcim/consoleport.html:23 templates/dcim/consoleserverport.html:23 #: templates/dcim/device.html:14 templates/dcim/device.html:128 #: templates/dcim/device_edit.html:10 templates/dcim/devicebay.html:23 @@ -2795,7 +2793,7 @@ msgstr "" #: virtualization/forms/bulk_import.py:99 #: virtualization/forms/filtersets.py:124 #: virtualization/forms/model_forms.py:188 -#: virtualization/tables/virtualmachines.py:61 vpn/choices.py:44 +#: virtualization/tables/virtualmachines.py:70 vpn/choices.py:44 #: vpn/forms/bulk_import.py:86 vpn/forms/bulk_import.py:283 #: vpn/forms/filtersets.py:271 vpn/forms/model_forms.py:89 #: vpn/forms/model_forms.py:124 vpn/forms/model_forms.py:237 @@ -2934,7 +2932,7 @@ msgid "Wireless role" msgstr "" #: dcim/forms/bulk_edit.py:1178 dcim/forms/model_forms.py:595 -#: dcim/forms/model_forms.py:1026 dcim/tables/devices.py:337 +#: dcim/forms/model_forms.py:1026 dcim/tables/devices.py:341 #: templates/dcim/consoleport.html:27 templates/dcim/consoleserverport.html:27 #: templates/dcim/frontport.html:27 templates/dcim/interface.html:35 #: templates/dcim/module.html:51 templates/dcim/modulebay.html:57 @@ -2943,7 +2941,7 @@ msgstr "" msgid "Module" msgstr "" -#: dcim/forms/bulk_edit.py:1305 dcim/tables/devices.py:685 +#: dcim/forms/bulk_edit.py:1305 dcim/tables/devices.py:689 #: templates/dcim/interface.html:113 msgid "LAG" msgstr "" @@ -2955,7 +2953,7 @@ msgstr "" #: dcim/forms/bulk_edit.py:1316 dcim/forms/bulk_import.py:659 #: dcim/forms/bulk_import.py:685 dcim/forms/filtersets.py:1163 #: dcim/forms/filtersets.py:1185 dcim/forms/filtersets.py:1258 -#: dcim/tables/devices.py:626 +#: dcim/tables/devices.py:630 #: templates/circuits/inc/circuit_termination.html:94 #: templates/dcim/consoleport.html:43 templates/dcim/consoleserverport.html:43 msgid "Speed" @@ -2980,13 +2978,13 @@ msgid "VLAN group" msgstr "" #: dcim/forms/bulk_edit.py:1361 dcim/forms/model_forms.py:1164 -#: dcim/tables/devices.py:599 virtualization/forms/bulk_edit.py:247 +#: dcim/tables/devices.py:603 virtualization/forms/bulk_edit.py:247 #: virtualization/forms/model_forms.py:329 msgid "Untagged VLAN" msgstr "" #: dcim/forms/bulk_edit.py:1369 dcim/forms/model_forms.py:1173 -#: dcim/tables/devices.py:605 virtualization/forms/bulk_edit.py:255 +#: dcim/tables/devices.py:609 virtualization/forms/bulk_edit.py:255 #: virtualization/forms/model_forms.py:338 msgid "Tagged VLANs" msgstr "" @@ -2996,7 +2994,7 @@ msgid "Wireless LAN group" msgstr "" #: dcim/forms/bulk_edit.py:1384 dcim/forms/model_forms.py:1151 -#: dcim/tables/devices.py:635 netbox/navigation/menu.py:134 +#: dcim/tables/devices.py:639 netbox/navigation/menu.py:134 #: templates/dcim/interface.html:289 wireless/tables/wirelesslan.py:24 msgid "Wireless LANs" msgstr "" @@ -3167,9 +3165,9 @@ msgid "Virtual chassis" msgstr "" #: dcim/forms/bulk_import.py:462 dcim/forms/model_forms.py:457 -#: dcim/tables/devices.py:231 extras/filtersets.py:501 +#: dcim/tables/devices.py:235 extras/filtersets.py:501 #: extras/forms/filtersets.py:330 ipam/forms/bulk_edit.py:478 -#: ipam/forms/model_forms.py:588 templates/dcim/device.html:239 +#: ipam/forms/model_forms.py:592 templates/dcim/device.html:239 #: templates/virtualization/cluster.html:11 #: templates/virtualization/virtualmachine.html:92 #: templates/virtualization/virtualmachine.html:102 @@ -3179,7 +3177,7 @@ msgstr "" #: virtualization/forms/filtersets.py:196 #: virtualization/forms/model_forms.py:82 #: virtualization/forms/model_forms.py:179 -#: virtualization/tables/virtualmachines.py:57 +#: virtualization/tables/virtualmachines.py:66 msgid "Cluster" msgstr "" @@ -3356,7 +3354,7 @@ msgstr "" msgid "Physical medium classification" msgstr "" -#: dcim/forms/bulk_import.py:973 dcim/tables/devices.py:846 +#: dcim/forms/bulk_import.py:973 dcim/tables/devices.py:850 msgid "Installed device" msgstr "" @@ -3444,7 +3442,7 @@ msgid "{side_upper} side termination not found: {device} {name}" msgstr "" #: dcim/forms/bulk_import.py:1244 dcim/forms/model_forms.py:696 -#: dcim/tables/devices.py:1033 templates/dcim/device.html:130 +#: dcim/tables/devices.py:1037 templates/dcim/device.html:130 #: templates/dcim/virtualchassis.html:28 templates/dcim/virtualchassis.html:60 msgid "Master" msgstr "" @@ -3566,7 +3564,7 @@ msgstr "" #: dcim/forms/filtersets.py:1155 dcim/forms/filtersets.py:1177 #: dcim/forms/filtersets.py:1199 dcim/forms/filtersets.py:1216 -#: dcim/forms/filtersets.py:1236 dcim/tables/devices.py:372 +#: dcim/forms/filtersets.py:1236 dcim/tables/devices.py:376 #: templates/dcim/consoleport.html:59 templates/dcim/consoleserverport.html:59 #: templates/dcim/frontport.html:74 templates/dcim/interface.html:146 #: templates/dcim/powerfeed.html:118 templates/dcim/poweroutlet.html:63 @@ -3580,7 +3578,7 @@ msgid "Virtual Device Context" msgstr "" #: dcim/forms/filtersets.py:1248 extras/forms/bulk_edit.py:315 -#: extras/forms/bulk_import.py:239 extras/forms/filtersets.py:479 +#: extras/forms/bulk_import.py:245 extras/forms/filtersets.py:479 #: extras/forms/model_forms.py:557 extras/tables/tables.py:487 #: templates/extras/journalentry.html:33 msgid "Kind" @@ -3612,7 +3610,7 @@ msgid "Transmit power (dBm)" msgstr "" #: dcim/forms/filtersets.py:1344 dcim/forms/filtersets.py:1366 -#: dcim/tables/devices.py:344 templates/dcim/cable.html:12 +#: dcim/tables/devices.py:348 templates/dcim/cable.html:12 #: templates/dcim/cable_edit.html:46 templates/dcim/cable_trace.html:43 #: templates/dcim/frontport.html:84 #: templates/dcim/inc/connection_endpoints.html:4 @@ -3620,7 +3618,7 @@ msgstr "" msgid "Cable" msgstr "" -#: dcim/forms/filtersets.py:1434 dcim/tables/devices.py:956 +#: dcim/forms/filtersets.py:1434 dcim/tables/devices.py:960 msgid "Discovered" msgstr "" @@ -3665,7 +3663,7 @@ msgstr "" msgid "Device Role" msgstr "" -#: dcim/forms/model_forms.py:428 dcim/models/devices.py:632 +#: dcim/forms/model_forms.py:428 dcim/models/devices.py:634 msgid "The lowest-numbered unit occupied by the device" msgstr "" @@ -3716,9 +3714,7 @@ msgstr "" #: templates/wireless/wirelesslink.html:10 #: templates/wireless/wirelesslink.html:49 #: virtualization/forms/model_forms.py:351 vpn/forms/bulk_import.py:297 -#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 -#: vpn/forms/model_forms.py:241 vpn/forms/model_forms.py:436 -#: vpn/forms/model_forms.py:445 vpn/tables/tunnels.py:91 +#: vpn/forms/model_forms.py:436 vpn/forms/model_forms.py:445 #: wireless/forms/model_forms.py:112 wireless/forms/model_forms.py:152 msgid "Interface" msgstr "" @@ -3789,7 +3785,7 @@ msgid "" msgstr "" #: dcim/forms/object_create.py:109 dcim/forms/object_create.py:270 -#: dcim/tables/devices.py:281 +#: dcim/tables/devices.py:285 msgid "Rear ports" msgstr "" @@ -3818,7 +3814,7 @@ msgid "" "selected number of rear port positions ({rearport_count})." msgstr "" -#: dcim/forms/object_create.py:408 dcim/tables/devices.py:1039 +#: dcim/forms/object_create.py:408 dcim/tables/devices.py:1043 #: ipam/tables/fhrp.py:31 templates/dcim/virtualchassis.html:54 #: templates/dcim/virtualchassis_edit.html:48 templates/ipam/fhrpgroup.html:39 msgid "Members" @@ -4501,13 +4497,13 @@ msgstr "" msgid "inventory item roles" msgstr "" -#: dcim/models/device_components.py:1230 dcim/models/devices.py:595 -#: dcim/models/devices.py:1173 dcim/models/racks.py:113 +#: dcim/models/device_components.py:1230 dcim/models/devices.py:597 +#: dcim/models/devices.py:1178 dcim/models/racks.py:113 msgid "serial number" msgstr "" -#: dcim/models/device_components.py:1238 dcim/models/devices.py:603 -#: dcim/models/devices.py:1180 dcim/models/racks.py:120 +#: dcim/models/device_components.py:1238 dcim/models/devices.py:605 +#: dcim/models/devices.py:1185 dcim/models/racks.py:120 msgid "asset tag" msgstr "" @@ -4555,7 +4551,7 @@ msgstr "" msgid "manufacturers" msgstr "" -#: dcim/models/devices.py:82 dcim/models/devices.py:381 +#: dcim/models/devices.py:82 dcim/models/devices.py:382 msgid "model" msgstr "" @@ -4563,11 +4559,11 @@ msgstr "" msgid "default platform" msgstr "" -#: dcim/models/devices.py:98 dcim/models/devices.py:385 +#: dcim/models/devices.py:98 dcim/models/devices.py:386 msgid "part number" msgstr "" -#: dcim/models/devices.py:101 dcim/models/devices.py:388 +#: dcim/models/devices.py:101 dcim/models/devices.py:389 msgid "Discrete part number (optional)" msgstr "" @@ -4601,7 +4597,7 @@ msgid "" "device type is neither a parent nor a child." msgstr "" -#: dcim/models/devices.py:128 dcim/models/devices.py:647 +#: dcim/models/devices.py:128 dcim/models/devices.py:649 msgid "airflow" msgstr "" @@ -4613,289 +4609,289 @@ msgstr "" msgid "device types" msgstr "" -#: dcim/models/devices.py:289 +#: dcim/models/devices.py:290 msgid "U height must be in increments of 0.5 rack units." msgstr "" -#: dcim/models/devices.py:306 +#: dcim/models/devices.py:307 #, python-brace-format msgid "" "Device {device} in rack {rack} does not have sufficient space to accommodate " "a height of {height}U" msgstr "" -#: dcim/models/devices.py:321 +#: dcim/models/devices.py:322 #, python-brace-format msgid "" "Unable to set 0U height: Found {racked_instance_count} " "instances already mounted within racks." msgstr "" -#: dcim/models/devices.py:330 +#: dcim/models/devices.py:331 msgid "" "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." msgstr "" -#: dcim/models/devices.py:336 +#: dcim/models/devices.py:337 msgid "Child device types must be 0U." msgstr "" -#: dcim/models/devices.py:404 +#: dcim/models/devices.py:405 msgid "module type" msgstr "" -#: dcim/models/devices.py:405 +#: dcim/models/devices.py:406 msgid "module types" msgstr "" -#: dcim/models/devices.py:473 +#: dcim/models/devices.py:475 msgid "Virtual machines may be assigned to this role" msgstr "" -#: dcim/models/devices.py:485 +#: dcim/models/devices.py:487 msgid "device role" msgstr "" -#: dcim/models/devices.py:486 +#: dcim/models/devices.py:488 msgid "device roles" msgstr "" -#: dcim/models/devices.py:503 +#: dcim/models/devices.py:505 msgid "Optionally limit this platform to devices of a certain manufacturer" msgstr "" -#: dcim/models/devices.py:515 +#: dcim/models/devices.py:517 msgid "platform" msgstr "" -#: dcim/models/devices.py:516 +#: dcim/models/devices.py:518 msgid "platforms" msgstr "" -#: dcim/models/devices.py:564 +#: dcim/models/devices.py:566 msgid "The function this device serves" msgstr "" -#: dcim/models/devices.py:596 +#: dcim/models/devices.py:598 msgid "Chassis serial number, assigned by the manufacturer" msgstr "" -#: dcim/models/devices.py:604 dcim/models/devices.py:1181 +#: dcim/models/devices.py:606 dcim/models/devices.py:1186 msgid "A unique tag used to identify this device" msgstr "" -#: dcim/models/devices.py:631 +#: dcim/models/devices.py:633 msgid "position (U)" msgstr "" -#: dcim/models/devices.py:638 +#: dcim/models/devices.py:640 msgid "rack face" msgstr "" -#: dcim/models/devices.py:658 dcim/models/devices.py:1390 +#: dcim/models/devices.py:660 dcim/models/devices.py:1395 #: virtualization/models/virtualmachines.py:98 msgid "primary IPv4" msgstr "" -#: dcim/models/devices.py:666 dcim/models/devices.py:1398 +#: dcim/models/devices.py:668 dcim/models/devices.py:1403 #: virtualization/models/virtualmachines.py:106 msgid "primary IPv6" msgstr "" -#: dcim/models/devices.py:674 +#: dcim/models/devices.py:676 msgid "out-of-band IP" msgstr "" -#: dcim/models/devices.py:691 +#: dcim/models/devices.py:693 msgid "VC position" msgstr "" -#: dcim/models/devices.py:695 +#: dcim/models/devices.py:697 msgid "Virtual chassis position" msgstr "" -#: dcim/models/devices.py:698 +#: dcim/models/devices.py:700 msgid "VC priority" msgstr "" -#: dcim/models/devices.py:702 +#: dcim/models/devices.py:704 msgid "Virtual chassis master election priority" msgstr "" -#: dcim/models/devices.py:705 dcim/models/sites.py:207 +#: dcim/models/devices.py:707 dcim/models/sites.py:207 msgid "latitude" msgstr "" -#: dcim/models/devices.py:710 dcim/models/devices.py:718 +#: dcim/models/devices.py:712 dcim/models/devices.py:720 #: dcim/models/sites.py:212 dcim/models/sites.py:220 msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgstr "" -#: dcim/models/devices.py:713 dcim/models/sites.py:215 +#: dcim/models/devices.py:715 dcim/models/sites.py:215 msgid "longitude" msgstr "" -#: dcim/models/devices.py:786 +#: dcim/models/devices.py:788 msgid "Device name must be unique per site." msgstr "" -#: dcim/models/devices.py:797 ipam/models/services.py:75 +#: dcim/models/devices.py:799 ipam/models/services.py:75 msgid "device" msgstr "" -#: dcim/models/devices.py:798 +#: dcim/models/devices.py:800 msgid "devices" msgstr "" -#: dcim/models/devices.py:838 +#: dcim/models/devices.py:840 #, python-brace-format msgid "Rack {rack} does not belong to site {site}." msgstr "" -#: dcim/models/devices.py:843 +#: dcim/models/devices.py:845 #, python-brace-format msgid "Location {location} does not belong to site {site}." msgstr "" -#: dcim/models/devices.py:849 +#: dcim/models/devices.py:851 #, python-brace-format msgid "Rack {rack} does not belong to location {location}." msgstr "" -#: dcim/models/devices.py:856 +#: dcim/models/devices.py:858 msgid "Cannot select a rack face without assigning a rack." msgstr "" -#: dcim/models/devices.py:860 +#: dcim/models/devices.py:862 msgid "Cannot select a rack position without assigning a rack." msgstr "" -#: dcim/models/devices.py:866 +#: dcim/models/devices.py:868 msgid "Position must be in increments of 0.5 rack units." msgstr "" -#: dcim/models/devices.py:870 +#: dcim/models/devices.py:872 msgid "Must specify rack face when defining rack position." msgstr "" -#: dcim/models/devices.py:878 +#: dcim/models/devices.py:880 #, python-brace-format msgid "A 0U device type ({device_type}) cannot be assigned to a rack position." msgstr "" -#: dcim/models/devices.py:889 +#: dcim/models/devices.py:891 msgid "" "Child device types cannot be assigned to a rack face. This is an attribute " "of the parent device." msgstr "" -#: dcim/models/devices.py:896 +#: dcim/models/devices.py:898 msgid "" "Child device types cannot be assigned to a rack position. This is an " "attribute of the parent device." msgstr "" -#: dcim/models/devices.py:910 +#: dcim/models/devices.py:912 #, python-brace-format msgid "" "U{position} is already occupied or does not have sufficient space to " "accommodate this device type: {device_type} ({u_height}U)" msgstr "" -#: dcim/models/devices.py:925 +#: dcim/models/devices.py:927 #, python-brace-format msgid "{ip} is not an IPv4 address." msgstr "" -#: dcim/models/devices.py:934 dcim/models/devices.py:949 +#: dcim/models/devices.py:936 dcim/models/devices.py:951 #, python-brace-format msgid "The specified IP address ({ip}) is not assigned to this device." msgstr "" -#: dcim/models/devices.py:940 +#: dcim/models/devices.py:942 #, python-brace-format msgid "{ip} is not an IPv6 address." msgstr "" -#: dcim/models/devices.py:967 +#: dcim/models/devices.py:969 #, python-brace-format msgid "" "The assigned platform is limited to {platform_manufacturer} device types, " "but this device's type belongs to {devicetype_manufacturer}." msgstr "" -#: dcim/models/devices.py:978 +#: dcim/models/devices.py:980 #, python-brace-format msgid "The assigned cluster belongs to a different site ({site})" msgstr "" -#: dcim/models/devices.py:986 +#: dcim/models/devices.py:988 msgid "A device assigned to a virtual chassis must have its position defined." msgstr "" -#: dcim/models/devices.py:1188 +#: dcim/models/devices.py:1193 msgid "module" msgstr "" -#: dcim/models/devices.py:1189 +#: dcim/models/devices.py:1194 msgid "modules" msgstr "" -#: dcim/models/devices.py:1205 +#: dcim/models/devices.py:1210 #, python-brace-format msgid "" "Module must be installed within a module bay belonging to the assigned " "device ({device})." msgstr "" -#: dcim/models/devices.py:1309 +#: dcim/models/devices.py:1314 msgid "domain" msgstr "" -#: dcim/models/devices.py:1322 dcim/models/devices.py:1323 +#: dcim/models/devices.py:1327 dcim/models/devices.py:1328 msgid "virtual chassis" msgstr "" -#: dcim/models/devices.py:1338 +#: dcim/models/devices.py:1343 #, python-brace-format msgid "The selected master ({master}) is not assigned to this virtual chassis." msgstr "" -#: dcim/models/devices.py:1354 +#: dcim/models/devices.py:1359 #, python-brace-format msgid "" "Unable to delete virtual chassis {self}. There are member interfaces which " "form a cross-chassis LAG interfaces." msgstr "" -#: dcim/models/devices.py:1379 vpn/models/l2vpn.py:37 +#: dcim/models/devices.py:1384 vpn/models/l2vpn.py:37 msgid "identifier" msgstr "" -#: dcim/models/devices.py:1380 +#: dcim/models/devices.py:1385 msgid "Numeric identifier unique to the parent device" msgstr "" -#: dcim/models/devices.py:1408 extras/models/models.py:129 +#: dcim/models/devices.py:1413 extras/models/models.py:129 #: extras/models/models.py:724 netbox/models/__init__.py:114 msgid "comments" msgstr "" -#: dcim/models/devices.py:1424 +#: dcim/models/devices.py:1429 msgid "virtual device context" msgstr "" -#: dcim/models/devices.py:1425 +#: dcim/models/devices.py:1430 msgid "virtual device contexts" msgstr "" -#: dcim/models/devices.py:1457 +#: dcim/models/devices.py:1462 #, python-brace-format msgid "{ip} is not an IPv{family} address." msgstr "" -#: dcim/models/devices.py:1463 +#: dcim/models/devices.py:1468 msgid "Primary IP address must belong to an interface on the assigned device." msgstr "" @@ -5269,7 +5265,7 @@ msgstr "" msgid "Reachable" msgstr "" -#: dcim/tables/connections.py:46 dcim/tables/devices.py:529 +#: dcim/tables/connections.py:46 dcim/tables/devices.py:533 #: templates/dcim/inventoryitem_edit.html:64 templates/dcim/poweroutlet.html:47 #: templates/dcim/powerport.html:18 msgid "Power Port" @@ -5288,7 +5284,7 @@ msgstr "" msgid "VMs" msgstr "" -#: dcim/tables/devices.py:133 dcim/tables/devices.py:245 +#: dcim/tables/devices.py:133 dcim/tables/devices.py:249 #: extras/forms/model_forms.py:515 templates/dcim/device.html:114 #: templates/dcim/device/render_config.html:11 #: templates/dcim/device/render_config.html:15 @@ -5297,62 +5293,62 @@ msgstr "" #: templates/virtualization/virtualmachine.html:47 #: templates/virtualization/virtualmachine/render_config.html:11 #: templates/virtualization/virtualmachine/render_config.html:15 -#: virtualization/tables/virtualmachines.py:93 +#: virtualization/tables/virtualmachines.py:106 msgid "Config Template" msgstr "" -#: dcim/tables/devices.py:216 dcim/tables/devices.py:1074 +#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 #: ipam/forms/bulk_import.py:511 ipam/forms/model_forms.py:296 #: ipam/tables/ip.py:352 ipam/tables/ip.py:418 ipam/tables/ip.py:441 #: templates/ipam/ipaddress.html:12 templates/ipam/ipaddress_edit.html:14 -#: virtualization/tables/virtualmachines.py:81 +#: virtualization/tables/virtualmachines.py:94 msgid "IP Address" msgstr "" -#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 -#: virtualization/tables/virtualmachines.py:72 +#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 +#: virtualization/tables/virtualmachines.py:85 msgid "IPv4 Address" msgstr "" -#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 -#: virtualization/tables/virtualmachines.py:76 +#: dcim/tables/devices.py:228 dcim/tables/devices.py:1086 +#: virtualization/tables/virtualmachines.py:89 msgid "IPv6 Address" msgstr "" -#: dcim/tables/devices.py:239 +#: dcim/tables/devices.py:243 msgid "VC Position" msgstr "" -#: dcim/tables/devices.py:242 +#: dcim/tables/devices.py:246 msgid "VC Priority" msgstr "" -#: dcim/tables/devices.py:249 templates/dcim/device_edit.html:38 +#: dcim/tables/devices.py:253 templates/dcim/device_edit.html:38 #: templates/dcim/devicebay_populate.html:16 msgid "Parent Device" msgstr "" -#: dcim/tables/devices.py:254 +#: dcim/tables/devices.py:258 msgid "Position (Device Bay)" msgstr "" -#: dcim/tables/devices.py:263 +#: dcim/tables/devices.py:267 msgid "Console ports" msgstr "" -#: dcim/tables/devices.py:266 +#: dcim/tables/devices.py:270 msgid "Console server ports" msgstr "" -#: dcim/tables/devices.py:269 +#: dcim/tables/devices.py:273 msgid "Power ports" msgstr "" -#: dcim/tables/devices.py:272 +#: dcim/tables/devices.py:276 msgid "Power outlets" msgstr "" -#: dcim/tables/devices.py:275 dcim/tables/devices.py:1087 +#: dcim/tables/devices.py:279 dcim/tables/devices.py:1091 #: dcim/tables/devicetypes.py:125 dcim/views.py:1005 dcim/views.py:1244 #: dcim/views.py:1930 netbox/navigation/menu.py:82 #: netbox/navigation/menu.py:238 templates/dcim/device/base.html:37 @@ -5362,53 +5358,53 @@ msgstr "" #: templates/dcim/virtualdevicecontext.html:85 #: templates/virtualization/virtualmachine/base.html:27 #: templates/virtualization/virtualmachine_list.html:14 -#: virtualization/tables/virtualmachines.py:87 virtualization/views.py:368 +#: virtualization/tables/virtualmachines.py:100 virtualization/views.py:368 #: wireless/tables/wirelesslan.py:55 msgid "Interfaces" msgstr "" -#: dcim/tables/devices.py:278 +#: dcim/tables/devices.py:282 msgid "Front ports" msgstr "" -#: dcim/tables/devices.py:284 +#: dcim/tables/devices.py:288 msgid "Device bays" msgstr "" -#: dcim/tables/devices.py:287 +#: dcim/tables/devices.py:291 msgid "Module bays" msgstr "" -#: dcim/tables/devices.py:290 +#: dcim/tables/devices.py:294 msgid "Inventory items" msgstr "" -#: dcim/tables/devices.py:329 dcim/tables/modules.py:56 +#: dcim/tables/devices.py:333 dcim/tables/modules.py:56 #: templates/dcim/modulebay.html:17 msgid "Module Bay" msgstr "" -#: dcim/tables/devices.py:350 +#: dcim/tables/devices.py:354 msgid "Cable Color" msgstr "" -#: dcim/tables/devices.py:356 +#: dcim/tables/devices.py:360 msgid "Link Peers" msgstr "" -#: dcim/tables/devices.py:359 +#: dcim/tables/devices.py:363 msgid "Mark Connected" msgstr "" -#: dcim/tables/devices.py:475 +#: dcim/tables/devices.py:479 msgid "Maximum draw (W)" msgstr "" -#: dcim/tables/devices.py:478 +#: dcim/tables/devices.py:482 msgid "Allocated draw (W)" msgstr "" -#: dcim/tables/devices.py:578 ipam/forms/model_forms.py:707 +#: dcim/tables/devices.py:582 ipam/forms/model_forms.py:711 #: ipam/tables/fhrp.py:28 ipam/views.py:597 ipam/views.py:691 #: netbox/navigation/menu.py:146 netbox/navigation/menu.py:148 #: templates/dcim/interface.html:351 templates/ipam/ipaddress_bulk_add.html:15 @@ -5417,12 +5413,12 @@ msgstr "" msgid "IP Addresses" msgstr "" -#: dcim/tables/devices.py:584 netbox/navigation/menu.py:190 +#: dcim/tables/devices.py:588 netbox/navigation/menu.py:190 #: templates/ipam/inc/panels/fhrp_groups.html:5 msgid "FHRP Groups" msgstr "" -#: dcim/tables/devices.py:596 templates/dcim/interface.html:90 +#: dcim/tables/devices.py:600 templates/dcim/interface.html:90 #: templates/virtualization/vminterface.html:70 templates/vpn/tunnel.html:18 #: templates/vpn/tunneltermination.html:14 vpn/forms/bulk_edit.py:75 #: vpn/forms/bulk_import.py:76 vpn/forms/filtersets.py:41 @@ -5431,20 +5427,20 @@ msgstr "" msgid "Tunnel" msgstr "" -#: dcim/tables/devices.py:621 dcim/tables/devicetypes.py:224 +#: dcim/tables/devices.py:625 dcim/tables/devicetypes.py:224 #: templates/dcim/interface.html:66 msgid "Management Only" msgstr "" -#: dcim/tables/devices.py:629 +#: dcim/tables/devices.py:633 msgid "Wireless link" msgstr "" -#: dcim/tables/devices.py:639 +#: dcim/tables/devices.py:643 msgid "VDCs" msgstr "" -#: dcim/tables/devices.py:647 dcim/tables/devicetypes.py:48 +#: dcim/tables/devices.py:651 dcim/tables/devicetypes.py:48 #: dcim/tables/devicetypes.py:140 dcim/views.py:1080 dcim/views.py:2023 #: netbox/navigation/menu.py:91 templates/dcim/device/base.html:52 #: templates/dcim/device_list.html:71 templates/dcim/devicetype/base.html:49 @@ -5453,7 +5449,7 @@ msgstr "" msgid "Inventory Items" msgstr "" -#: dcim/tables/devices.py:728 +#: dcim/tables/devices.py:732 #: templates/circuits/inc/circuit_termination.html:80 #: templates/dcim/consoleport.html:81 templates/dcim/consoleserverport.html:81 #: templates/dcim/frontport.html:53 templates/dcim/frontport.html:125 @@ -5462,28 +5458,28 @@ msgstr "" msgid "Rear Port" msgstr "" -#: dcim/tables/devices.py:893 templates/dcim/modulebay.html:51 +#: dcim/tables/devices.py:897 templates/dcim/modulebay.html:51 msgid "Installed Module" msgstr "" -#: dcim/tables/devices.py:896 +#: dcim/tables/devices.py:900 msgid "Module Serial" msgstr "" -#: dcim/tables/devices.py:900 +#: dcim/tables/devices.py:904 msgid "Module Asset Tag" msgstr "" -#: dcim/tables/devices.py:909 +#: dcim/tables/devices.py:913 msgid "Module Status" msgstr "" -#: dcim/tables/devices.py:951 dcim/tables/devicetypes.py:308 +#: dcim/tables/devices.py:955 dcim/tables/devicetypes.py:308 #: templates/dcim/inventoryitem.html:41 msgid "Component" msgstr "" -#: dcim/tables/devices.py:1006 +#: dcim/tables/devices.py:1010 msgid "Items" msgstr "" @@ -6041,7 +6037,7 @@ msgid "Cluster type (slug)" msgstr "" #: extras/filtersets.py:490 ipam/forms/bulk_edit.py:475 -#: ipam/forms/model_forms.py:585 virtualization/forms/filtersets.py:108 +#: ipam/forms/model_forms.py:589 virtualization/forms/filtersets.py:108 msgid "Cluster group" msgstr "" @@ -6170,8 +6166,8 @@ msgid "Is active" msgstr "" #: extras/forms/bulk_import.py:34 extras/forms/bulk_import.py:115 -#: extras/forms/bulk_import.py:130 extras/forms/bulk_import.py:153 -#: extras/forms/bulk_import.py:177 extras/forms/filtersets.py:114 +#: extras/forms/bulk_import.py:136 extras/forms/bulk_import.py:159 +#: extras/forms/bulk_import.py:183 extras/forms/filtersets.py:114 #: extras/forms/filtersets.py:160 extras/forms/filtersets.py:201 #: extras/forms/model_forms.py:43 extras/forms/model_forms.py:127 #: extras/forms/model_forms.py:156 extras/forms/model_forms.py:197 @@ -6180,8 +6176,8 @@ msgid "Content types" msgstr "" #: extras/forms/bulk_import.py:36 extras/forms/bulk_import.py:117 -#: extras/forms/bulk_import.py:132 extras/forms/bulk_import.py:155 -#: extras/forms/bulk_import.py:179 tenancy/forms/bulk_import.py:96 +#: extras/forms/bulk_import.py:138 extras/forms/bulk_import.py:161 +#: extras/forms/bulk_import.py:185 tenancy/forms/bulk_import.py:96 msgid "One or more assigned object types" msgstr "" @@ -6225,29 +6221,38 @@ msgid "" "separated by colon: \"choice1:First Choice,choice2:Second Choice\"" msgstr "" -#: extras/forms/bulk_import.py:182 +#: extras/forms/bulk_import.py:120 extras/models/models.py:353 +msgid "button class" +msgstr "" + +#: extras/forms/bulk_import.py:123 extras/models/models.py:357 +msgid "" +"The class of the first link in a group will be used for the dropdown button" +msgstr "" + +#: extras/forms/bulk_import.py:188 msgid "Action object" msgstr "" -#: extras/forms/bulk_import.py:184 +#: extras/forms/bulk_import.py:190 msgid "Webhook name or script as dotted path module.Class" msgstr "" -#: extras/forms/bulk_import.py:205 +#: extras/forms/bulk_import.py:211 #, python-brace-format msgid "Webhook {name} not found" msgstr "" -#: extras/forms/bulk_import.py:214 +#: extras/forms/bulk_import.py:220 #, python-brace-format msgid "Script {name} not found" msgstr "" -#: extras/forms/bulk_import.py:236 +#: extras/forms/bulk_import.py:242 msgid "Assigned object type" msgstr "" -#: extras/forms/bulk_import.py:241 +#: extras/forms/bulk_import.py:247 msgid "The classification of entry" msgstr "" @@ -7129,15 +7134,6 @@ msgstr "" msgid "Links with the same group will appear as a dropdown menu" msgstr "" -#: extras/models/models.py:353 -msgid "button class" -msgstr "" - -#: extras/models/models.py:357 -msgid "" -"The class of the first link in a group will be used for the dropdown button" -msgstr "" - #: extras/models/models.py:360 msgid "new window" msgstr "" @@ -7321,7 +7317,7 @@ msgid "staged changes" msgstr "" #: extras/models/tags.py:40 -msgid "The object type(s) to which this this tag can be applied." +msgid "The object type(s) to which this tag can be applied." msgstr "" #: extras/models/tags.py:49 @@ -7620,7 +7616,7 @@ msgid "VLAN number (1-4094)" msgstr "" #: ipam/filtersets.py:437 ipam/filtersets.py:441 ipam/filtersets.py:533 -#: ipam/forms/model_forms.py:444 templates/tenancy/contact.html:54 +#: ipam/forms/model_forms.py:430 templates/tenancy/contact.html:54 #: tenancy/forms/bulk_edit.py:112 msgid "Address" msgstr "" @@ -7789,7 +7785,7 @@ msgid "Authentication key" msgstr "" #: ipam/forms/bulk_edit.py:404 ipam/forms/filtersets.py:369 -#: ipam/forms/model_forms.py:455 netbox/navigation/menu.py:376 +#: ipam/forms/model_forms.py:441 netbox/navigation/menu.py:376 #: templates/ipam/fhrpgroup.html:51 #: templates/wireless/inc/authentication_attrs.html:5 #: wireless/forms/bulk_edit.py:90 wireless/forms/bulk_edit.py:137 @@ -7806,11 +7802,11 @@ msgstr "" msgid "Maximum child VLAN VID" msgstr "" -#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:527 +#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:531 msgid "Scope type" msgstr "" -#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:600 +#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:604 #: ipam/tables/vlans.py:71 templates/ipam/vlangroup.html:39 msgid "Scope" msgstr "" @@ -7819,8 +7815,8 @@ msgstr "" msgid "Site & Group" msgstr "" -#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:663 -#: ipam/forms/model_forms.py:697 ipam/tables/services.py:19 +#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:667 +#: ipam/forms/model_forms.py:701 ipam/tables/services.py:19 #: ipam/tables/services.py:49 templates/ipam/service.html:39 #: templates/ipam/servicetemplate.html:24 msgid "Ports" @@ -7859,7 +7855,7 @@ msgid "Parent device of assigned interface (if any)" msgstr "" #: ipam/forms/bulk_import.py:310 ipam/forms/bulk_import.py:496 -#: ipam/forms/model_forms.py:691 virtualization/filtersets.py:284 +#: ipam/forms/model_forms.py:695 virtualization/filtersets.py:284 #: virtualization/filtersets.py:323 virtualization/forms/bulk_edit.py:199 #: virtualization/forms/bulk_edit.py:325 #: virtualization/forms/bulk_import.py:146 @@ -8034,8 +8030,8 @@ msgstr "" #: virtualization/forms/filtersets.py:189 #: virtualization/forms/filtersets.py:234 #: virtualization/forms/model_forms.py:223 -#: virtualization/tables/virtualmachines.py:115 -#: virtualization/tables/virtualmachines.py:168 vpn/choices.py:45 +#: virtualization/tables/virtualmachines.py:128 +#: virtualization/tables/virtualmachines.py:181 vpn/choices.py:45 #: vpn/forms/filtersets.py:289 vpn/forms/model_forms.py:161 #: vpn/forms/model_forms.py:172 vpn/forms/model_forms.py:274 msgid "Virtual Machine" @@ -8058,7 +8054,7 @@ msgstr "" msgid "IP Range" msgstr "" -#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:454 +#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:440 #: templates/ipam/fhrpgroup.html:19 templates/ipam/ipaddress_edit.html:52 msgid "FHRP Group" msgstr "" @@ -8071,7 +8067,7 @@ msgstr "" msgid "An IP address can only be assigned to a single object." msgstr "" -#: ipam/forms/model_forms.py:357 ipam/models/ip.py:877 +#: ipam/forms/model_forms.py:357 ipam/models/ip.py:896 msgid "" "Cannot reassign IP address while it is designated as the primary IP for the " "parent object" @@ -8082,45 +8078,39 @@ msgid "" "Only IP addresses assigned to an interface can be designated as primary IPs." msgstr "" -#: ipam/forms/model_forms.py:373 -#, python-brace-format -msgid "{ip} is a network ID, which may not be assigned to an interface." -msgstr "" - -#: ipam/forms/model_forms.py:379 -#, python-brace-format -msgid "{ip} is a broadcast address, which may not be assigned to an interface." -msgstr "" - -#: ipam/forms/model_forms.py:456 +#: ipam/forms/model_forms.py:442 msgid "Virtual IP Address" msgstr "" -#: ipam/forms/model_forms.py:598 ipam/forms/model_forms.py:637 +#: ipam/forms/model_forms.py:523 +msgid "Assignment already exists" +msgstr "" + +#: ipam/forms/model_forms.py:602 ipam/forms/model_forms.py:641 #: ipam/tables/ip.py:250 templates/ipam/vlan_edit.html:37 #: templates/ipam/vlangroup.html:27 msgid "VLAN Group" msgstr "" -#: ipam/forms/model_forms.py:599 +#: ipam/forms/model_forms.py:603 msgid "Child VLANs" msgstr "" -#: ipam/forms/model_forms.py:668 ipam/forms/model_forms.py:702 +#: ipam/forms/model_forms.py:672 ipam/forms/model_forms.py:706 msgid "" "Comma-separated list of one or more port numbers. A range may be specified " "using a hyphen." msgstr "" -#: ipam/forms/model_forms.py:673 templates/ipam/servicetemplate.html:12 +#: ipam/forms/model_forms.py:677 templates/ipam/servicetemplate.html:12 msgid "Service Template" msgstr "" -#: ipam/forms/model_forms.py:724 +#: ipam/forms/model_forms.py:728 msgid "Service template" msgstr "" -#: ipam/forms/model_forms.py:754 +#: ipam/forms/model_forms.py:758 msgid "" "Must specify name, protocol, and port(s) if not using a service template." msgstr "" @@ -8280,12 +8270,12 @@ msgstr "" msgid "Cannot create prefix with /0 mask." msgstr "" -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 #, python-brace-format msgid "VRF {vrf}" msgstr "" -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 msgid "global table" msgstr "" @@ -8378,12 +8368,22 @@ msgstr "" msgid "Cannot create IP address with /0 mask." msgstr "" -#: ipam/models/ip.py:856 +#: ipam/models/ip.py:850 +#, python-brace-format +msgid "{ip} is a network ID, which may not be assigned to an interface." +msgstr "" + +#: ipam/models/ip.py:861 +#, python-brace-format +msgid "{ip} is a broadcast address, which may not be assigned to an interface." +msgstr "" + +#: ipam/models/ip.py:875 #, python-brace-format msgid "Duplicate IP address found in {table}: {ipaddress}" msgstr "" -#: ipam/models/ip.py:883 +#: ipam/models/ip.py:902 msgid "Only IPv6 addresses can be assigned SLAAC status" msgstr "" @@ -9141,7 +9141,7 @@ msgstr "" #: templates/virtualization/virtualmachine.html:177 #: templates/virtualization/virtualmachine/base.html:32 #: templates/virtualization/virtualmachine_list.html:21 -#: virtualization/tables/virtualmachines.py:90 virtualization/views.py:389 +#: virtualization/tables/virtualmachines.py:103 virtualization/views.py:389 msgid "Virtual Disks" msgstr "" @@ -10054,7 +10054,7 @@ msgstr "" #: templates/core/job.html:66 #, python-format -msgid "every %(interval)s seconds" +msgid "every %(interval)s minutes" msgstr "" #: templates/dcim/bulk_disconnect.html:9 @@ -12525,95 +12525,95 @@ msgstr "" msgid "Invalid filter for {model}: {error}" msgstr "" -#: users/models.py:54 +#: users/models.py:55 msgid "user" msgstr "" -#: users/models.py:55 +#: users/models.py:56 msgid "users" msgstr "" -#: users/models.py:66 +#: users/models.py:67 msgid "A user with this username already exists." msgstr "" -#: users/models.py:78 vpn/models/crypto.py:42 +#: users/models.py:79 vpn/models/crypto.py:42 msgid "group" msgstr "" -#: users/models.py:79 +#: users/models.py:80 msgid "groups" msgstr "" -#: users/models.py:106 users/models.py:107 +#: users/models.py:107 users/models.py:108 msgid "user preferences" msgstr "" -#: users/models.py:174 +#: users/models.py:175 #, python-brace-format msgid "Key '{path}' is a leaf node; cannot assign new keys" msgstr "" -#: users/models.py:186 +#: users/models.py:187 #, python-brace-format msgid "Key '{path}' is a dictionary; cannot assign a non-dictionary value" msgstr "" -#: users/models.py:252 +#: users/models.py:253 msgid "expires" msgstr "" -#: users/models.py:257 +#: users/models.py:258 msgid "last used" msgstr "" -#: users/models.py:262 +#: users/models.py:263 msgid "key" msgstr "" -#: users/models.py:268 +#: users/models.py:269 msgid "write enabled" msgstr "" -#: users/models.py:270 +#: users/models.py:271 msgid "Permit create/update/delete operations using this key" msgstr "" -#: users/models.py:281 +#: users/models.py:282 msgid "allowed IPs" msgstr "" -#: users/models.py:283 +#: users/models.py:284 msgid "" "Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for " "no restrictions. Ex: \"10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64\"" msgstr "" -#: users/models.py:291 +#: users/models.py:296 msgid "token" msgstr "" -#: users/models.py:292 +#: users/models.py:297 msgid "tokens" msgstr "" -#: users/models.py:373 +#: users/models.py:378 msgid "The list of actions granted by this permission" msgstr "" -#: users/models.py:378 +#: users/models.py:383 msgid "constraints" msgstr "" -#: users/models.py:379 +#: users/models.py:384 msgid "Queryset filter matching the applicable objects of the selected type(s)" msgstr "" -#: users/models.py:386 +#: users/models.py:391 msgid "permission" msgstr "" -#: users/models.py:387 +#: users/models.py:392 msgid "permissions" msgstr "" @@ -12860,43 +12860,50 @@ msgid "" "the object's change log for details." msgstr "" -#: utilities/forms/utils.py:42 utilities/forms/utils.py:65 -#: utilities/forms/utils.py:77 utilities/forms/utils.py:80 +#: utilities/forms/utils.py:42 utilities/forms/utils.py:68 +#: utilities/forms/utils.py:85 utilities/forms/utils.py:87 #, python-brace-format msgid "Range \"{value}\" is invalid." msgstr "" -#: utilities/forms/utils.py:225 +#: utilities/forms/utils.py:74 +#, python-brace-format +msgid "" +"Invalid range: Ending value ({end}) must be greater than beginning value " +"({begin})." +msgstr "" + +#: utilities/forms/utils.py:232 #, python-brace-format msgid "Duplicate or conflicting column header for \"{field}\"" msgstr "" -#: utilities/forms/utils.py:231 +#: utilities/forms/utils.py:238 #, python-brace-format msgid "Duplicate or conflicting column header for \"{header}\"" msgstr "" -#: utilities/forms/utils.py:240 +#: utilities/forms/utils.py:247 #, python-brace-format msgid "Row {row}: Expected {count_expected} columns but found {count_found}" msgstr "" -#: utilities/forms/utils.py:263 +#: utilities/forms/utils.py:270 #, python-brace-format msgid "Unexpected column header \"{field}\" found." msgstr "" -#: utilities/forms/utils.py:265 +#: utilities/forms/utils.py:272 #, python-brace-format msgid "Column \"{field}\" is not a related object; cannot use dots" msgstr "" -#: utilities/forms/utils.py:269 +#: utilities/forms/utils.py:276 #, python-brace-format msgid "Invalid related object attribute for column \"{field}\": {to_field}" msgstr "" -#: utilities/forms/utils.py:277 +#: utilities/forms/utils.py:284 #, python-brace-format msgid "Required column header \"{header}\" not found." msgstr "" @@ -13502,6 +13509,11 @@ msgstr "" msgid "Assigned Object Type" msgstr "" +#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 +#: vpn/forms/model_forms.py:241 vpn/tables/tunnels.py:91 +msgid "Tunnel interface" +msgstr "" + #: vpn/forms/model_forms.py:147 msgid "First Termination" msgstr "" From ad0e476788891e41a9a81cc9d9cf88a2fa89f3d6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Apr 2024 16:06:42 -0400 Subject: [PATCH 029/303] Release v3.7.5 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- docs/release-notes/version-3.7.md | 2 +- netbox/netbox/settings.py | 2 +- netbox/translations/es/LC_MESSAGES/django.mo | Bin 218182 -> 218466 bytes netbox/translations/es/LC_MESSAGES/django.po | 804 +++++------ netbox/translations/fr/LC_MESSAGES/django.mo | Bin 220002 -> 220300 bytes netbox/translations/fr/LC_MESSAGES/django.po | 808 +++++------ netbox/translations/ja/LC_MESSAGES/django.mo | Bin 234165 -> 233614 bytes netbox/translations/ja/LC_MESSAGES/django.po | 1011 +++++++------- netbox/translations/pt/LC_MESSAGES/django.mo | Bin 215341 -> 215621 bytes netbox/translations/pt/LC_MESSAGES/django.po | 804 +++++------ netbox/translations/ru/LC_MESSAGES/django.mo | Bin 276912 -> 275547 bytes netbox/translations/ru/LC_MESSAGES/django.po | 1277 +++++++++--------- netbox/translations/tr/LC_MESSAGES/django.mo | Bin 209700 -> 209985 bytes netbox/translations/tr/LC_MESSAGES/django.po | 800 +++++------ requirements.txt | 18 +- 17 files changed, 2803 insertions(+), 2727 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 44c797241..d4a9bab72 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -26,7 +26,7 @@ body: attributes: label: NetBox Version description: What version of NetBox are you currently running? - placeholder: v3.7.4 + placeholder: v3.7.5 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index a550bff57..2cee040f8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.7.4 + placeholder: v3.7.5 validations: required: true - type: dropdown diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index 15159b4f3..ddbeb4bca 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -1,6 +1,6 @@ # NetBox v3.7 -## v3.7.5 (FUTURE) +## v3.7.5 (2024-04-04) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index e662561ee..0ae43da66 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -28,7 +28,7 @@ from netbox.plugins import PluginConfig # Environment setup # -VERSION = '3.7.5-dev' +VERSION = '3.7.5' # Hostname HOSTNAME = platform.node() diff --git a/netbox/translations/es/LC_MESSAGES/django.mo b/netbox/translations/es/LC_MESSAGES/django.mo index 6ea16e7ff15ea4a5230ee891d1ee6250df9eb88e..ef13cd45b54415eeaeceeff87f6dda810a424f86 100644 GIT binary patch delta 61626 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
    @YMou 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 delta 61372 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 " @@ -4781,7 +4777,7 @@ msgstr "" "href=\"{url}\">{racked_instance_count} instancias ya está montado dentro" " de bastidores." -#: dcim/models/devices.py:330 +#: dcim/models/devices.py:331 msgid "" "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." @@ -4790,155 +4786,155 @@ msgstr "" "asociadas a este dispositivo antes de desclasificarlo como dispositivo " "principal." -#: dcim/models/devices.py:336 +#: dcim/models/devices.py:337 msgid "Child device types must be 0U." msgstr "Los tipos de dispositivos secundarios deben ser 0U." -#: dcim/models/devices.py:404 +#: dcim/models/devices.py:405 msgid "module type" msgstr "tipo de módulo" -#: dcim/models/devices.py:405 +#: dcim/models/devices.py:406 msgid "module types" msgstr "tipos de módulos" -#: dcim/models/devices.py:473 +#: dcim/models/devices.py:475 msgid "Virtual machines may be assigned to this role" msgstr "Se pueden asignar máquinas virtuales a esta función" -#: dcim/models/devices.py:485 +#: dcim/models/devices.py:487 msgid "device role" msgstr "rol del dispositivo" -#: dcim/models/devices.py:486 +#: dcim/models/devices.py:488 msgid "device roles" msgstr "funciones del dispositivo" -#: dcim/models/devices.py:503 +#: dcim/models/devices.py:505 msgid "Optionally limit this platform to devices of a certain manufacturer" msgstr "" "Si lo desea, limite esta plataforma a dispositivos de un fabricante " "determinado." -#: dcim/models/devices.py:515 +#: dcim/models/devices.py:517 msgid "platform" msgstr "plataforma" -#: dcim/models/devices.py:516 +#: dcim/models/devices.py:518 msgid "platforms" msgstr "plataformas" -#: dcim/models/devices.py:564 +#: dcim/models/devices.py:566 msgid "The function this device serves" msgstr "La función que cumple este dispositivo" -#: dcim/models/devices.py:596 +#: dcim/models/devices.py:598 msgid "Chassis serial number, assigned by the manufacturer" msgstr "Número de serie del chasis, asignado por el fabricante" -#: dcim/models/devices.py:604 dcim/models/devices.py:1181 +#: dcim/models/devices.py:606 dcim/models/devices.py:1186 msgid "A unique tag used to identify this device" msgstr "Una etiqueta única que se utiliza para identificar este dispositivo" -#: dcim/models/devices.py:631 +#: dcim/models/devices.py:633 msgid "position (U)" msgstr "posición (U)" -#: dcim/models/devices.py:638 +#: dcim/models/devices.py:640 msgid "rack face" msgstr "cara del estante" -#: dcim/models/devices.py:658 dcim/models/devices.py:1390 +#: dcim/models/devices.py:660 dcim/models/devices.py:1395 #: virtualization/models/virtualmachines.py:98 msgid "primary IPv4" msgstr "IPv4 principal" -#: dcim/models/devices.py:666 dcim/models/devices.py:1398 +#: dcim/models/devices.py:668 dcim/models/devices.py:1403 #: virtualization/models/virtualmachines.py:106 msgid "primary IPv6" msgstr "IPv6 principal" -#: dcim/models/devices.py:674 +#: dcim/models/devices.py:676 msgid "out-of-band IP" msgstr "IP fuera de banda" -#: dcim/models/devices.py:691 +#: dcim/models/devices.py:693 msgid "VC position" msgstr "Posición VC" -#: dcim/models/devices.py:695 +#: dcim/models/devices.py:697 msgid "Virtual chassis position" msgstr "Posición virtual del chasis" -#: dcim/models/devices.py:698 +#: dcim/models/devices.py:700 msgid "VC priority" msgstr "Prioridad VC" -#: dcim/models/devices.py:702 +#: dcim/models/devices.py:704 msgid "Virtual chassis master election priority" msgstr "Prioridad de elección del maestro del chasis virtual" -#: dcim/models/devices.py:705 dcim/models/sites.py:207 +#: dcim/models/devices.py:707 dcim/models/sites.py:207 msgid "latitude" msgstr "latitud" -#: dcim/models/devices.py:710 dcim/models/devices.py:718 +#: dcim/models/devices.py:712 dcim/models/devices.py:720 #: dcim/models/sites.py:212 dcim/models/sites.py:220 msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgstr "Coordenada GPS en formato decimal (xx.aaaaa)" -#: dcim/models/devices.py:713 dcim/models/sites.py:215 +#: dcim/models/devices.py:715 dcim/models/sites.py:215 msgid "longitude" msgstr "longitud" -#: dcim/models/devices.py:786 +#: dcim/models/devices.py:788 msgid "Device name must be unique per site." msgstr "El nombre del dispositivo debe ser único por sitio." -#: dcim/models/devices.py:797 ipam/models/services.py:75 +#: dcim/models/devices.py:799 ipam/models/services.py:75 msgid "device" msgstr "dispositivo" -#: dcim/models/devices.py:798 +#: dcim/models/devices.py:800 msgid "devices" msgstr "dispositivos" -#: dcim/models/devices.py:838 +#: dcim/models/devices.py:840 #, python-brace-format msgid "Rack {rack} does not belong to site {site}." msgstr "Estante {rack} no pertenece al sitio {site}." -#: dcim/models/devices.py:843 +#: dcim/models/devices.py:845 #, python-brace-format msgid "Location {location} does not belong to site {site}." msgstr "Ubicación {location} no pertenece al sitio {site}." -#: dcim/models/devices.py:849 +#: dcim/models/devices.py:851 #, python-brace-format msgid "Rack {rack} does not belong to location {location}." msgstr "Estante {rack} no pertenece a la ubicación {location}." -#: dcim/models/devices.py:856 +#: dcim/models/devices.py:858 msgid "Cannot select a rack face without assigning a rack." msgstr "No se puede seleccionar una cara de bastidor sin asignar un bastidor." -#: dcim/models/devices.py:860 +#: dcim/models/devices.py:862 msgid "Cannot select a rack position without assigning a rack." msgstr "" "No se puede seleccionar una posición de cremallera sin asignar una " "cremallera." -#: dcim/models/devices.py:866 +#: dcim/models/devices.py:868 msgid "Position must be in increments of 0.5 rack units." msgstr "La posición debe estar en incrementos de 0,5 unidades de estante." -#: dcim/models/devices.py:870 +#: dcim/models/devices.py:872 msgid "Must specify rack face when defining rack position." msgstr "" "Debe especificar la cara de la cremallera al definir la posición de la " "cremallera." -#: dcim/models/devices.py:878 +#: dcim/models/devices.py:880 #, python-brace-format msgid "" "A 0U device type ({device_type}) cannot be assigned to a rack position." @@ -4946,7 +4942,7 @@ msgstr "" "Un tipo de dispositivo 0U ({device_type}) no se puede asignar a una posición" " de estantería." -#: dcim/models/devices.py:889 +#: dcim/models/devices.py:891 msgid "" "Child device types cannot be assigned to a rack face. This is an attribute " "of the parent device." @@ -4954,7 +4950,7 @@ msgstr "" "Los tipos de dispositivos secundarios no se pueden asignar a la cara de un " "bastidor. Este es un atributo del dispositivo principal." -#: dcim/models/devices.py:896 +#: dcim/models/devices.py:898 msgid "" "Child device types cannot be assigned to a rack position. This is an " "attribute of the parent device." @@ -4962,7 +4958,7 @@ msgstr "" "Los tipos de dispositivos secundarios no se pueden asignar a una posición de" " bastidor. Este es un atributo del dispositivo principal." -#: dcim/models/devices.py:910 +#: dcim/models/devices.py:912 #, python-brace-format msgid "" "U{position} is already occupied or does not have sufficient space to " @@ -4971,23 +4967,23 @@ msgstr "" "U{position} ya está ocupado o no tiene espacio suficiente para este tipo de " "dispositivo: {device_type} ({u_height}U)" -#: dcim/models/devices.py:925 +#: dcim/models/devices.py:927 #, python-brace-format msgid "{ip} is not an IPv4 address." msgstr "{ip} no es una dirección IPv4." -#: dcim/models/devices.py:934 dcim/models/devices.py:949 +#: dcim/models/devices.py:936 dcim/models/devices.py:951 #, python-brace-format msgid "The specified IP address ({ip}) is not assigned to this device." msgstr "" "La dirección IP especificada ({ip}) no está asignado a este dispositivo." -#: dcim/models/devices.py:940 +#: dcim/models/devices.py:942 #, python-brace-format msgid "{ip} is not an IPv6 address." msgstr "{ip} no es una dirección IPv6." -#: dcim/models/devices.py:967 +#: dcim/models/devices.py:969 #, python-brace-format msgid "" "The assigned platform is limited to {platform_manufacturer} device types, " @@ -4997,25 +4993,25 @@ msgstr "" "dispositivos, pero el tipo de este dispositivo pertenece a " "{devicetype_manufacturer}." -#: dcim/models/devices.py:978 +#: dcim/models/devices.py:980 #, python-brace-format msgid "The assigned cluster belongs to a different site ({site})" msgstr "El clúster asignado pertenece a un sitio diferente ({site})" -#: dcim/models/devices.py:986 +#: dcim/models/devices.py:988 msgid "A device assigned to a virtual chassis must have its position defined." msgstr "" "Un dispositivo asignado a un chasis virtual debe tener su posición definida." -#: dcim/models/devices.py:1188 +#: dcim/models/devices.py:1193 msgid "module" msgstr "módulo" -#: dcim/models/devices.py:1189 +#: dcim/models/devices.py:1194 msgid "modules" msgstr "módulos" -#: dcim/models/devices.py:1205 +#: dcim/models/devices.py:1210 #, python-brace-format msgid "" "Module must be installed within a module bay belonging to the assigned " @@ -5024,22 +5020,22 @@ msgstr "" "El módulo debe instalarse en un compartimiento de módulos que pertenezca al " "dispositivo asignado ({device})." -#: dcim/models/devices.py:1309 +#: dcim/models/devices.py:1314 msgid "domain" msgstr "dominio" -#: dcim/models/devices.py:1322 dcim/models/devices.py:1323 +#: dcim/models/devices.py:1327 dcim/models/devices.py:1328 msgid "virtual chassis" msgstr "chasis virtual" -#: dcim/models/devices.py:1338 +#: dcim/models/devices.py:1343 #, python-brace-format msgid "" "The selected master ({master}) is not assigned to this virtual chassis." msgstr "" "El maestro seleccionado ({master}) no está asignado a este chasis virtual." -#: dcim/models/devices.py:1354 +#: dcim/models/devices.py:1359 #, python-brace-format msgid "" "Unable to delete virtual chassis {self}. There are member interfaces which " @@ -5048,33 +5044,33 @@ msgstr "" "No se puede eliminar el chasis virtual {self}. Hay interfaces miembros que " "forman interfaces LAG entre chasis." -#: dcim/models/devices.py:1379 vpn/models/l2vpn.py:37 +#: dcim/models/devices.py:1384 vpn/models/l2vpn.py:37 msgid "identifier" msgstr "identificador" -#: dcim/models/devices.py:1380 +#: dcim/models/devices.py:1385 msgid "Numeric identifier unique to the parent device" msgstr "Identificador numérico exclusivo del dispositivo principal" -#: dcim/models/devices.py:1408 extras/models/models.py:129 +#: dcim/models/devices.py:1413 extras/models/models.py:129 #: extras/models/models.py:724 netbox/models/__init__.py:114 msgid "comments" msgstr "comentarios" -#: dcim/models/devices.py:1424 +#: dcim/models/devices.py:1429 msgid "virtual device context" msgstr "contexto de dispositivo virtual" -#: dcim/models/devices.py:1425 +#: dcim/models/devices.py:1430 msgid "virtual device contexts" msgstr "contextos de dispositivos virtuales" -#: dcim/models/devices.py:1457 +#: dcim/models/devices.py:1462 #, python-brace-format msgid "{ip} is not an IPv{family} address." msgstr "{ip} no es un IPv{family} dirección." -#: dcim/models/devices.py:1463 +#: dcim/models/devices.py:1468 msgid "Primary IP address must belong to an interface on the assigned device." msgstr "" "La dirección IP principal debe pertenecer a una interfaz del dispositivo " @@ -5465,7 +5461,7 @@ msgstr "Puerto de consola" msgid "Reachable" msgstr "Accesible" -#: dcim/tables/connections.py:46 dcim/tables/devices.py:529 +#: dcim/tables/connections.py:46 dcim/tables/devices.py:533 #: templates/dcim/inventoryitem_edit.html:64 #: templates/dcim/poweroutlet.html:47 templates/dcim/powerport.html:18 msgid "Power Port" @@ -5484,7 +5480,7 @@ msgstr "Dispositivos" msgid "VMs" msgstr "VM" -#: dcim/tables/devices.py:133 dcim/tables/devices.py:245 +#: dcim/tables/devices.py:133 dcim/tables/devices.py:249 #: extras/forms/model_forms.py:515 templates/dcim/device.html:114 #: templates/dcim/device/render_config.html:11 #: templates/dcim/device/render_config.html:15 @@ -5493,62 +5489,62 @@ msgstr "VM" #: templates/virtualization/virtualmachine.html:47 #: templates/virtualization/virtualmachine/render_config.html:11 #: templates/virtualization/virtualmachine/render_config.html:15 -#: virtualization/tables/virtualmachines.py:93 +#: virtualization/tables/virtualmachines.py:106 msgid "Config Template" msgstr "Plantilla de configuración" -#: dcim/tables/devices.py:216 dcim/tables/devices.py:1074 +#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 #: ipam/forms/bulk_import.py:511 ipam/forms/model_forms.py:296 #: ipam/tables/ip.py:352 ipam/tables/ip.py:418 ipam/tables/ip.py:441 #: templates/ipam/ipaddress.html:12 templates/ipam/ipaddress_edit.html:14 -#: virtualization/tables/virtualmachines.py:81 +#: virtualization/tables/virtualmachines.py:94 msgid "IP Address" msgstr "Dirección IP" -#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 -#: virtualization/tables/virtualmachines.py:72 +#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 +#: virtualization/tables/virtualmachines.py:85 msgid "IPv4 Address" msgstr "Dirección IPv4" -#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 -#: virtualization/tables/virtualmachines.py:76 +#: dcim/tables/devices.py:228 dcim/tables/devices.py:1086 +#: virtualization/tables/virtualmachines.py:89 msgid "IPv6 Address" msgstr "Dirección IPv6" -#: dcim/tables/devices.py:239 +#: dcim/tables/devices.py:243 msgid "VC Position" msgstr "Posición VC" -#: dcim/tables/devices.py:242 +#: dcim/tables/devices.py:246 msgid "VC Priority" msgstr "Prioridad VC" -#: dcim/tables/devices.py:249 templates/dcim/device_edit.html:38 +#: dcim/tables/devices.py:253 templates/dcim/device_edit.html:38 #: templates/dcim/devicebay_populate.html:16 msgid "Parent Device" msgstr "Dispositivo principal" -#: dcim/tables/devices.py:254 +#: dcim/tables/devices.py:258 msgid "Position (Device Bay)" msgstr "Posición (bahía de dispositivos)" -#: dcim/tables/devices.py:263 +#: dcim/tables/devices.py:267 msgid "Console ports" msgstr "Puertos de consola" -#: dcim/tables/devices.py:266 +#: dcim/tables/devices.py:270 msgid "Console server ports" msgstr "Puertos de servidor de consola" -#: dcim/tables/devices.py:269 +#: dcim/tables/devices.py:273 msgid "Power ports" msgstr "Puertos de alimentación" -#: dcim/tables/devices.py:272 +#: dcim/tables/devices.py:276 msgid "Power outlets" msgstr "tomas de corriente" -#: dcim/tables/devices.py:275 dcim/tables/devices.py:1087 +#: dcim/tables/devices.py:279 dcim/tables/devices.py:1091 #: dcim/tables/devicetypes.py:125 dcim/views.py:1005 dcim/views.py:1244 #: dcim/views.py:1930 netbox/navigation/menu.py:82 #: netbox/navigation/menu.py:238 templates/dcim/device/base.html:37 @@ -5558,53 +5554,53 @@ msgstr "tomas de corriente" #: templates/dcim/virtualdevicecontext.html:85 #: templates/virtualization/virtualmachine/base.html:27 #: templates/virtualization/virtualmachine_list.html:14 -#: virtualization/tables/virtualmachines.py:87 virtualization/views.py:368 +#: virtualization/tables/virtualmachines.py:100 virtualization/views.py:368 #: wireless/tables/wirelesslan.py:55 msgid "Interfaces" msgstr "Interfaces" -#: dcim/tables/devices.py:278 +#: dcim/tables/devices.py:282 msgid "Front ports" msgstr "Puertos frontales" -#: dcim/tables/devices.py:284 +#: dcim/tables/devices.py:288 msgid "Device bays" msgstr "Compartimentos para dispositivos" -#: dcim/tables/devices.py:287 +#: dcim/tables/devices.py:291 msgid "Module bays" msgstr "Bahías de módulos" -#: dcim/tables/devices.py:290 +#: dcim/tables/devices.py:294 msgid "Inventory items" msgstr "Artículos de inventario" -#: dcim/tables/devices.py:329 dcim/tables/modules.py:56 +#: dcim/tables/devices.py:333 dcim/tables/modules.py:56 #: templates/dcim/modulebay.html:17 msgid "Module Bay" msgstr "Bahía de módulos" -#: dcim/tables/devices.py:350 +#: dcim/tables/devices.py:354 msgid "Cable Color" msgstr "Color del cable" -#: dcim/tables/devices.py:356 +#: dcim/tables/devices.py:360 msgid "Link Peers" msgstr "Vincula a tus compañeros" -#: dcim/tables/devices.py:359 +#: dcim/tables/devices.py:363 msgid "Mark Connected" msgstr "Marcar conectado" -#: dcim/tables/devices.py:475 +#: dcim/tables/devices.py:479 msgid "Maximum draw (W)" msgstr "Consumo máximo (W)" -#: dcim/tables/devices.py:478 +#: dcim/tables/devices.py:482 msgid "Allocated draw (W)" msgstr "Sorteo asignado (W)" -#: dcim/tables/devices.py:578 ipam/forms/model_forms.py:707 +#: dcim/tables/devices.py:582 ipam/forms/model_forms.py:711 #: ipam/tables/fhrp.py:28 ipam/views.py:597 ipam/views.py:691 #: netbox/navigation/menu.py:146 netbox/navigation/menu.py:148 #: templates/dcim/interface.html:351 templates/ipam/ipaddress_bulk_add.html:15 @@ -5613,12 +5609,12 @@ msgstr "Sorteo asignado (W)" msgid "IP Addresses" msgstr "Direcciones IP" -#: dcim/tables/devices.py:584 netbox/navigation/menu.py:190 +#: dcim/tables/devices.py:588 netbox/navigation/menu.py:190 #: templates/ipam/inc/panels/fhrp_groups.html:5 msgid "FHRP Groups" msgstr "Grupos FHRP" -#: dcim/tables/devices.py:596 templates/dcim/interface.html:90 +#: dcim/tables/devices.py:600 templates/dcim/interface.html:90 #: templates/virtualization/vminterface.html:70 templates/vpn/tunnel.html:18 #: templates/vpn/tunneltermination.html:14 vpn/forms/bulk_edit.py:75 #: vpn/forms/bulk_import.py:76 vpn/forms/filtersets.py:41 @@ -5627,20 +5623,20 @@ msgstr "Grupos FHRP" msgid "Tunnel" msgstr "Túnel" -#: dcim/tables/devices.py:621 dcim/tables/devicetypes.py:224 +#: dcim/tables/devices.py:625 dcim/tables/devicetypes.py:224 #: templates/dcim/interface.html:66 msgid "Management Only" msgstr "Solo administración" -#: dcim/tables/devices.py:629 +#: dcim/tables/devices.py:633 msgid "Wireless link" msgstr "Enlace inalámbrico" -#: dcim/tables/devices.py:639 +#: dcim/tables/devices.py:643 msgid "VDCs" msgstr "VDC" -#: dcim/tables/devices.py:647 dcim/tables/devicetypes.py:48 +#: dcim/tables/devices.py:651 dcim/tables/devicetypes.py:48 #: dcim/tables/devicetypes.py:140 dcim/views.py:1080 dcim/views.py:2023 #: netbox/navigation/menu.py:91 templates/dcim/device/base.html:52 #: templates/dcim/device_list.html:71 templates/dcim/devicetype/base.html:49 @@ -5649,7 +5645,7 @@ msgstr "VDC" msgid "Inventory Items" msgstr "Artículos de inventario" -#: dcim/tables/devices.py:728 +#: dcim/tables/devices.py:732 #: templates/circuits/inc/circuit_termination.html:80 #: templates/dcim/consoleport.html:81 templates/dcim/consoleserverport.html:81 #: templates/dcim/frontport.html:53 templates/dcim/frontport.html:125 @@ -5658,28 +5654,28 @@ msgstr "Artículos de inventario" msgid "Rear Port" msgstr "Puerto trasero" -#: dcim/tables/devices.py:893 templates/dcim/modulebay.html:51 +#: dcim/tables/devices.py:897 templates/dcim/modulebay.html:51 msgid "Installed Module" msgstr "Módulo instalado" -#: dcim/tables/devices.py:896 +#: dcim/tables/devices.py:900 msgid "Module Serial" msgstr "Serie del módulo" -#: dcim/tables/devices.py:900 +#: dcim/tables/devices.py:904 msgid "Module Asset Tag" msgstr "Etiqueta de activo del módulo" -#: dcim/tables/devices.py:909 +#: dcim/tables/devices.py:913 msgid "Module Status" msgstr "Estado del módulo" -#: dcim/tables/devices.py:951 dcim/tables/devicetypes.py:308 +#: dcim/tables/devices.py:955 dcim/tables/devicetypes.py:308 #: templates/dcim/inventoryitem.html:41 msgid "Component" msgstr "Componente" -#: dcim/tables/devices.py:1006 +#: dcim/tables/devices.py:1010 msgid "Items" msgstr "Artículos" @@ -6244,7 +6240,7 @@ msgid "Cluster type (slug)" msgstr "Tipo de clúster (babosa)" #: extras/filtersets.py:490 ipam/forms/bulk_edit.py:475 -#: ipam/forms/model_forms.py:585 virtualization/forms/filtersets.py:108 +#: ipam/forms/model_forms.py:589 virtualization/forms/filtersets.py:108 msgid "Cluster group" msgstr "Grupo de clústeres" @@ -6375,8 +6371,8 @@ msgid "Is active" msgstr "Está activo" #: extras/forms/bulk_import.py:34 extras/forms/bulk_import.py:115 -#: extras/forms/bulk_import.py:130 extras/forms/bulk_import.py:153 -#: extras/forms/bulk_import.py:177 extras/forms/filtersets.py:114 +#: extras/forms/bulk_import.py:136 extras/forms/bulk_import.py:159 +#: extras/forms/bulk_import.py:183 extras/forms/filtersets.py:114 #: extras/forms/filtersets.py:160 extras/forms/filtersets.py:201 #: extras/forms/model_forms.py:43 extras/forms/model_forms.py:127 #: extras/forms/model_forms.py:156 extras/forms/model_forms.py:197 @@ -6385,8 +6381,8 @@ msgid "Content types" msgstr "Tipos de contenido" #: extras/forms/bulk_import.py:36 extras/forms/bulk_import.py:117 -#: extras/forms/bulk_import.py:132 extras/forms/bulk_import.py:155 -#: extras/forms/bulk_import.py:179 tenancy/forms/bulk_import.py:96 +#: extras/forms/bulk_import.py:138 extras/forms/bulk_import.py:161 +#: extras/forms/bulk_import.py:185 tenancy/forms/bulk_import.py:96 msgid "One or more assigned object types" msgstr "Uno o más tipos de objetos asignados" @@ -6434,29 +6430,39 @@ msgstr "" " opcionales separadas por dos puntos: «Choice1:First Choice, Choice2:Second " "Choice»" -#: extras/forms/bulk_import.py:182 +#: extras/forms/bulk_import.py:120 extras/models/models.py:353 +msgid "button class" +msgstr "clase de botones" + +#: extras/forms/bulk_import.py:123 extras/models/models.py:357 +msgid "" +"The class of the first link in a group will be used for the dropdown button" +msgstr "" +"La clase del primer enlace de un grupo se usará para el botón desplegable" + +#: extras/forms/bulk_import.py:188 msgid "Action object" msgstr "Objeto de acción" -#: extras/forms/bulk_import.py:184 +#: extras/forms/bulk_import.py:190 msgid "Webhook name or script as dotted path module.Class" msgstr "Nombre o script del webhook como ruta punteada module.Class" -#: extras/forms/bulk_import.py:205 +#: extras/forms/bulk_import.py:211 #, python-brace-format msgid "Webhook {name} not found" msgstr "Webhook {name} no se encontró" -#: extras/forms/bulk_import.py:214 +#: extras/forms/bulk_import.py:220 #, python-brace-format msgid "Script {name} not found" msgstr "Guión {name} no se encontró" -#: extras/forms/bulk_import.py:236 +#: extras/forms/bulk_import.py:242 msgid "Assigned object type" msgstr "Tipo de objeto asignado" -#: extras/forms/bulk_import.py:241 +#: extras/forms/bulk_import.py:247 msgid "The classification of entry" msgstr "La clasificación de entrada" @@ -7422,16 +7428,6 @@ msgstr "Código de plantilla Jinja2 para la URL del enlace" msgid "Links with the same group will appear as a dropdown menu" msgstr "Los enlaces con el mismo grupo aparecerán en un menú desplegable" -#: extras/models/models.py:353 -msgid "button class" -msgstr "clase de botones" - -#: extras/models/models.py:357 -msgid "" -"The class of the first link in a group will be used for the dropdown button" -msgstr "" -"La clase del primer enlace de un grupo se usará para el botón desplegable" - #: extras/models/models.py:360 msgid "new window" msgstr "ventana nueva" @@ -7621,7 +7617,7 @@ msgid "staged changes" msgstr "cambios por etapas" #: extras/models/tags.py:40 -msgid "The object type(s) to which this this tag can be applied." +msgid "The object type(s) to which this tag can be applied." msgstr "Los tipos de objeto a los que se puede aplicar esta etiqueta." #: extras/models/tags.py:49 @@ -7922,7 +7918,7 @@ msgid "VLAN number (1-4094)" msgstr "Número de VLAN (1-4094)" #: ipam/filtersets.py:437 ipam/filtersets.py:441 ipam/filtersets.py:533 -#: ipam/forms/model_forms.py:444 templates/tenancy/contact.html:54 +#: ipam/forms/model_forms.py:430 templates/tenancy/contact.html:54 #: tenancy/forms/bulk_edit.py:112 msgid "Address" msgstr "Dirección" @@ -8091,7 +8087,7 @@ msgid "Authentication key" msgstr "Clave de autenticación" #: ipam/forms/bulk_edit.py:404 ipam/forms/filtersets.py:369 -#: ipam/forms/model_forms.py:455 netbox/navigation/menu.py:376 +#: ipam/forms/model_forms.py:441 netbox/navigation/menu.py:376 #: templates/ipam/fhrpgroup.html:51 #: templates/wireless/inc/authentication_attrs.html:5 #: wireless/forms/bulk_edit.py:90 wireless/forms/bulk_edit.py:137 @@ -8108,11 +8104,11 @@ msgstr "VLAN (VID) secundaria mínima" msgid "Maximum child VLAN VID" msgstr "VLAN (VID) secundaria máxima" -#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:527 +#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:531 msgid "Scope type" msgstr "Tipo de ámbito" -#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:600 +#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:604 #: ipam/tables/vlans.py:71 templates/ipam/vlangroup.html:39 msgid "Scope" msgstr "Alcance" @@ -8121,8 +8117,8 @@ msgstr "Alcance" msgid "Site & Group" msgstr "Sitio y grupo" -#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:663 -#: ipam/forms/model_forms.py:697 ipam/tables/services.py:19 +#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:667 +#: ipam/forms/model_forms.py:701 ipam/tables/services.py:19 #: ipam/tables/services.py:49 templates/ipam/service.html:39 #: templates/ipam/servicetemplate.html:24 msgid "Ports" @@ -8162,7 +8158,7 @@ msgid "Parent device of assigned interface (if any)" msgstr "Dispositivo principal de la interfaz asignada (si existe)" #: ipam/forms/bulk_import.py:310 ipam/forms/bulk_import.py:496 -#: ipam/forms/model_forms.py:691 virtualization/filtersets.py:284 +#: ipam/forms/model_forms.py:695 virtualization/filtersets.py:284 #: virtualization/filtersets.py:323 virtualization/forms/bulk_edit.py:199 #: virtualization/forms/bulk_edit.py:325 #: virtualization/forms/bulk_import.py:146 @@ -8340,8 +8336,8 @@ msgstr "Puerto" #: virtualization/forms/filtersets.py:189 #: virtualization/forms/filtersets.py:234 #: virtualization/forms/model_forms.py:223 -#: virtualization/tables/virtualmachines.py:115 -#: virtualization/tables/virtualmachines.py:168 vpn/choices.py:45 +#: virtualization/tables/virtualmachines.py:128 +#: virtualization/tables/virtualmachines.py:181 vpn/choices.py:45 #: vpn/forms/filtersets.py:289 vpn/forms/model_forms.py:161 #: vpn/forms/model_forms.py:172 vpn/forms/model_forms.py:274 msgid "Virtual Machine" @@ -8364,7 +8360,7 @@ msgstr "Asignación de sitio/VLAN" msgid "IP Range" msgstr "Rango de IP" -#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:454 +#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:440 #: templates/ipam/fhrpgroup.html:19 templates/ipam/ipaddress_edit.html:52 msgid "FHRP Group" msgstr "Grupo FHRP" @@ -8377,7 +8373,7 @@ msgstr "Haga que esta sea la IP principal del dispositivo/VM" msgid "An IP address can only be assigned to a single object." msgstr "Solo se puede asignar una dirección IP a un único objeto." -#: ipam/forms/model_forms.py:357 ipam/models/ip.py:877 +#: ipam/forms/model_forms.py:357 ipam/models/ip.py:896 msgid "" "Cannot reassign IP address while it is designated as the primary IP for the " "parent object" @@ -8392,34 +8388,25 @@ msgstr "" "Solo las direcciones IP asignadas a una interfaz se pueden designar como IP " "principales." -#: ipam/forms/model_forms.py:373 -#, python-brace-format -msgid "{ip} is a network ID, which may not be assigned to an interface." -msgstr "{ip} es un ID de red, que no puede asignarse a una interfaz." - -#: ipam/forms/model_forms.py:379 -#, python-brace-format -msgid "" -"{ip} is a broadcast address, which may not be assigned to an interface." -msgstr "" -"{ip} es una dirección de transmisión, que puede no estar asignada a una " -"interfaz." - -#: ipam/forms/model_forms.py:456 +#: ipam/forms/model_forms.py:442 msgid "Virtual IP Address" msgstr "Dirección IP virtual" -#: ipam/forms/model_forms.py:598 ipam/forms/model_forms.py:637 +#: ipam/forms/model_forms.py:523 +msgid "Assignment already exists" +msgstr "La asignación ya existe" + +#: ipam/forms/model_forms.py:602 ipam/forms/model_forms.py:641 #: ipam/tables/ip.py:250 templates/ipam/vlan_edit.html:37 #: templates/ipam/vlangroup.html:27 msgid "VLAN Group" msgstr "Grupo VLAN" -#: ipam/forms/model_forms.py:599 +#: ipam/forms/model_forms.py:603 msgid "Child VLANs" msgstr "VLAN secundarias" -#: ipam/forms/model_forms.py:668 ipam/forms/model_forms.py:702 +#: ipam/forms/model_forms.py:672 ipam/forms/model_forms.py:706 msgid "" "Comma-separated list of one or more port numbers. A range may be specified " "using a hyphen." @@ -8427,15 +8414,15 @@ msgstr "" "Lista separada por comas de uno o más números de puerto. Se puede " "especificar un rango mediante un guión." -#: ipam/forms/model_forms.py:673 templates/ipam/servicetemplate.html:12 +#: ipam/forms/model_forms.py:677 templates/ipam/servicetemplate.html:12 msgid "Service Template" msgstr "Plantilla de servicio" -#: ipam/forms/model_forms.py:724 +#: ipam/forms/model_forms.py:728 msgid "Service template" msgstr "Plantilla de servicio" -#: ipam/forms/model_forms.py:754 +#: ipam/forms/model_forms.py:758 msgid "" "Must specify name, protocol, and port(s) if not using a service template." msgstr "" @@ -8603,12 +8590,12 @@ msgstr "prefijos" msgid "Cannot create prefix with /0 mask." msgstr "No se puede crear un prefijo con la máscara /0." -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 #, python-brace-format msgid "VRF {vrf}" msgstr "VRF {vrf}" -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 msgid "global table" msgstr "tabla global" @@ -8705,12 +8692,25 @@ msgstr "direcciones IP" msgid "Cannot create IP address with /0 mask." msgstr "No se puede crear una dirección IP con la máscara /0." -#: ipam/models/ip.py:856 +#: ipam/models/ip.py:850 +#, python-brace-format +msgid "{ip} is a network ID, which may not be assigned to an interface." +msgstr "{ip} es un ID de red, que no puede asignarse a una interfaz." + +#: ipam/models/ip.py:861 +#, python-brace-format +msgid "" +"{ip} is a broadcast address, which may not be assigned to an interface." +msgstr "" +"{ip} es una dirección de transmisión, que puede no estar asignada a una " +"interfaz." + +#: ipam/models/ip.py:875 #, python-brace-format msgid "Duplicate IP address found in {table}: {ipaddress}" msgstr "Se encontró una dirección IP duplicada en {table}: {ipaddress}" -#: ipam/models/ip.py:883 +#: ipam/models/ip.py:902 msgid "Only IPv6 addresses can be assigned SLAAC status" msgstr "Solo a las direcciones IPv6 se les puede asignar el estado SLAAC" @@ -9504,7 +9504,7 @@ msgstr "Máquinas virtuales" #: templates/virtualization/virtualmachine.html:177 #: templates/virtualization/virtualmachine/base.html:32 #: templates/virtualization/virtualmachine_list.html:21 -#: virtualization/tables/virtualmachines.py:90 virtualization/views.py:389 +#: virtualization/tables/virtualmachines.py:103 virtualization/views.py:389 msgid "Virtual Disks" msgstr "Discos virtuales" @@ -10431,8 +10431,8 @@ msgstr "Programación" #: templates/core/job.html:66 #, python-format -msgid "every %(interval)s seconds" -msgstr "cada %(interval)s segundos" +msgid "every %(interval)s minutes" +msgstr "cada %(interval)s minutos" #: templates/dcim/bulk_disconnect.html:9 #, python-format @@ -12994,68 +12994,68 @@ msgstr "Este tipo de objeto no admite restricciones." msgid "Invalid filter for {model}: {error}" msgstr "Filtro no válido para {model}: {error}" -#: users/models.py:54 +#: users/models.py:55 msgid "user" msgstr "usuario" -#: users/models.py:55 +#: users/models.py:56 msgid "users" msgstr "usuarios" -#: users/models.py:66 +#: users/models.py:67 msgid "A user with this username already exists." msgstr "Ya existe un usuario con este nombre de usuario." -#: users/models.py:78 vpn/models/crypto.py:42 +#: users/models.py:79 vpn/models/crypto.py:42 msgid "group" msgstr "grupo" -#: users/models.py:79 +#: users/models.py:80 msgid "groups" msgstr "grupos" -#: users/models.py:106 users/models.py:107 +#: users/models.py:107 users/models.py:108 msgid "user preferences" msgstr "preferencias de usuario" -#: users/models.py:174 +#: users/models.py:175 #, python-brace-format msgid "Key '{path}' is a leaf node; cannot assign new keys" msgstr "Clave '{path}'es un nodo de hoja; no se pueden asignar claves nuevas" -#: users/models.py:186 +#: users/models.py:187 #, python-brace-format msgid "Key '{path}' is a dictionary; cannot assign a non-dictionary value" msgstr "" "Clave '{path}'es un diccionario; no puede asignar un valor que no sea de " "diccionario" -#: users/models.py:252 +#: users/models.py:253 msgid "expires" msgstr "caduca" -#: users/models.py:257 +#: users/models.py:258 msgid "last used" msgstr "utilizado por última vez" -#: users/models.py:262 +#: users/models.py:263 msgid "key" msgstr "clave" -#: users/models.py:268 +#: users/models.py:269 msgid "write enabled" msgstr "escritura habilitada" -#: users/models.py:270 +#: users/models.py:271 msgid "Permit create/update/delete operations using this key" msgstr "" "Permitir operaciones de creación/actualización/eliminación con esta clave" -#: users/models.py:281 +#: users/models.py:282 msgid "allowed IPs" msgstr "IP permitidas" -#: users/models.py:283 +#: users/models.py:284 msgid "" "Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for" " no restrictions. Ex: \"10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64\"" @@ -13064,34 +13064,34 @@ msgstr "" "blanco para que no haya restricciones. Por ejemplo: «10.1.1.0/24, " "192.168.10.16/32, 2001:DB 8:1: :/64\"" -#: users/models.py:291 +#: users/models.py:296 msgid "token" msgstr "simbólico" -#: users/models.py:292 +#: users/models.py:297 msgid "tokens" msgstr "fichas" -#: users/models.py:373 +#: users/models.py:378 msgid "The list of actions granted by this permission" msgstr "La lista de acciones concedidas por este permiso" -#: users/models.py:378 +#: users/models.py:383 msgid "constraints" msgstr "restricciones" -#: users/models.py:379 +#: users/models.py:384 msgid "" "Queryset filter matching the applicable objects of the selected type(s)" msgstr "" "Filtro Queryset que coincide con los objetos aplicables de los tipos " "seleccionados" -#: users/models.py:386 +#: users/models.py:391 msgid "permission" msgstr "permiso" -#: users/models.py:387 +#: users/models.py:392 msgid "permissions" msgstr "permisos" @@ -13366,47 +13366,56 @@ msgstr "" "Este objeto se ha modificado desde que se renderizó el formulario. Consulte " "el registro de cambios del objeto para obtener más información." -#: utilities/forms/utils.py:42 utilities/forms/utils.py:65 -#: utilities/forms/utils.py:77 utilities/forms/utils.py:80 +#: utilities/forms/utils.py:42 utilities/forms/utils.py:68 +#: utilities/forms/utils.py:85 utilities/forms/utils.py:87 #, python-brace-format msgid "Range \"{value}\" is invalid." msgstr "Gama»{value}«no es válido." -#: utilities/forms/utils.py:225 +#: utilities/forms/utils.py:74 +#, python-brace-format +msgid "" +"Invalid range: Ending value ({end}) must be greater than beginning value " +"({begin})." +msgstr "" +"Intervalo no válido: valor final ({end}) debe ser mayor que el valor inicial" +" ({begin})." + +#: utilities/forms/utils.py:232 #, python-brace-format msgid "Duplicate or conflicting column header for \"{field}\"" msgstr "Cabecera de columna duplicada o conflictiva para»{field}»" -#: utilities/forms/utils.py:231 +#: utilities/forms/utils.py:238 #, python-brace-format msgid "Duplicate or conflicting column header for \"{header}\"" msgstr "Cabecera de columna duplicada o conflictiva para»{header}»" -#: utilities/forms/utils.py:240 +#: utilities/forms/utils.py:247 #, python-brace-format msgid "Row {row}: Expected {count_expected} columns but found {count_found}" msgstr "" "Fila {row}: Esperado {count_expected} columnas pero encontradas " "{count_found}" -#: utilities/forms/utils.py:263 +#: utilities/forms/utils.py:270 #, python-brace-format msgid "Unexpected column header \"{field}\" found." msgstr "Encabezado de columna inesperado»{field}«encontrado." -#: utilities/forms/utils.py:265 +#: utilities/forms/utils.py:272 #, python-brace-format msgid "Column \"{field}\" is not a related object; cannot use dots" msgstr "Columna»{field}\"no es un objeto relacionado; no puede usar puntos" -#: utilities/forms/utils.py:269 +#: utilities/forms/utils.py:276 #, python-brace-format msgid "Invalid related object attribute for column \"{field}\": {to_field}" msgstr "" "Atributo de objeto relacionado no válido para la columna»{field}«: " "{to_field}" -#: utilities/forms/utils.py:277 +#: utilities/forms/utils.py:284 #, python-brace-format msgid "Required column header \"{header}\" not found." msgstr "Encabezado de columna obligatorio»{header}«no se encontró." @@ -14048,6 +14057,11 @@ msgstr "Propuesta" msgid "Assigned Object Type" msgstr "Tipo de objeto asignado" +#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 +#: vpn/forms/model_forms.py:241 vpn/tables/tunnels.py:91 +msgid "Tunnel interface" +msgstr "Interfaz de túnel" + #: vpn/forms/model_forms.py:147 msgid "First Termination" msgstr "Primera rescisión" diff --git a/netbox/translations/fr/LC_MESSAGES/django.mo b/netbox/translations/fr/LC_MESSAGES/django.mo index 234a12ba9719719879dc2b6181f28054329d2887..b5dac2ef5cd89f65ae7147f48d886b2a5d232d7f 100644 GIT binary patch delta 61626 zcmXWkd7zC&AHeZ*??u_lQbd+(-}gOa%^q1oWQ!ye(JLvAr9~)`tO?mtp(JIiRAkAL z(t?y0Z7A(hdcWT@^ZxUhndf(N`CK3biM!XeY#Y?g1Pl-fXEQ`7Ddb|XC zA>Ai#3GK~D!g3!EAK0Bce&_)=P&26O!p0=p5dFGd49g$8s6t7H8?6NwslyE1-a5d}NiAIV_B+6hPbcv>*seTNz<6O*&OECjip-c1}CcWWh3c4Nb;N4h19P7t13+>-x zUObIg;zcZsSNt9JPBkn>y#ro_!_W*ofVpuxx)&Cp0j&C){2S@|c*CoBIrW{ z(2mZc9sM57xDfVCUbLU0Xll!#_xC_&+!uZB7Icq|#Qb>w1@fPd!mM~_FZOQ;tSWlFDf)aTG_}2A`@q;fDz=Z0?GMHFNs<*0*D2>c63Tr9>i)K$9M|wA29g(G<2p zJMM}OG#H)PaCB|&MKiW2UVi~SJsZ&Hw_#s=H`dFhrKJX}fo7rsI+6C+fbkREDA>Vc zXzJ#pGhZF+ThV~_VpIGKi(s+zv{a@V;%w@zaVZ|fEAYOI(9f*s3S3P4YuE+5XGu#W zyHQv}!Q*lfopIKzA;O$!DzCss*gn>$Vg>4NU|IYgO?{qhX{qDZ4xggl5l!`vXurRr z?~(IpMlWVdP&ui*T4CkSnXEk1n+tAEiK-V;*P&hrgu{QM*==JXCUg?W2*+?vi$q5vEch5y< zb_hLwiNaxqSD*v8Lp$n)Mt%!=-&{1n=g>{}3Oa$U(cNfZN6>ygN8b}ah3m=0MG8ig z^Xia+LRgl188pHU*dGVsAl!+*LMs+YOZ3LJ=pI>#2C^=?8QrAspn;#l>Ub6_WAUP9 znDf_xf;V(UJLrwhY%n^LacF=KV=H_TP33X)zO!fm>BT|@az^u_GcOn|fljCr+D{A2 z>iM4*Z)d*x&7f|q25kLg}$Kd+$uZby&zE_8E$j85b$ z^fdgE$@w?(3MImYMrg{~M{h(oO+U1w5oqKSv>Cs=Srf1)kFhsjrMzE zygmrc)ZIx6c5pxXz-)A&)zR0`8~31@c|W!vLzn0@y0(8tvzHF3z8YQYa_Amthz8Oo zws%LzOAd`UB;yS;;tdPq4eQXgdmC%vK{TL@GNI#~Xr``0m$E!MaKmUPbmjxm07gY8 zhW2FQF$zYwIQm??;ni5*8S96mUqsKM1O9`~ zXol{t!1=d>`)DvV6VVw@k3NY8wj}yII?$`=b8n*e?~K)*xezo7wKjP1EA zhR>Kn6*>QY6KP6=1Ko=rzX#FHH5aeOHRyml(fjtI0e=)diLI!g#@bk`Quv8!2>L~3 zCAu_Up=*C04Ja*HIUJun=!Hx)fb!_lG>G-qSe$xKw4<@;hsk4D7FVH5vmbr_b4=|8 z^uBCW!YL?*ek#_C^<*~+)oB=l&UgX(Zhry&o_-vM;P2=;?^`wOg*&k(^+)kKd=ouZ z*{X%LZHnIC8Z)s6R>ZOBM3-Y7zyCiUZ#aprWqS3H+FWRg3Zk2)EIPBw@p@x)DcVH4 zp_{6Itlx%aa5TDSCPk;AOZNok^!z_f!PGq$-IltLNE|>n(@}KbZ)5vku{}$T&{1A& z%Jm{>Kz+~*-j2R_7R2_oSep7~EQKd*_xz{V3(1F{b19e09Mn7~(7horRwl?R#428dF@R$^<6E@S$Xh+YWsalN& zv=9BBe;5tuvbtgB1<`;?qXRaL?d{R$dZL@PA9_4zBbzVrZe7lQB?|x2&>bt(3ms2H zf8+588ptj*6Z_BsK1Bog8r{ujFcW`Ae|#@cKlEDzy}u8-iHF4YJJFv77bYpRqHqLV ziXsid&vLcVj@zRHcSYaX1EOQl4yL2O;oOC$b}zQUPthePc};i^R7Pjs9PQ^GwEyG` z3a?R^gEjE_hT)x`L?2ie{R;igc2%R0nNHZ0`V90ZsQp+K^E6IN+>K4pjBZ5x$$4#< zSW`3;J&*vBi9Qruqg&C)hNH)A5_%eD$M%)z8*K{~!9(cJdS}oS=WG&AO#!sOlITDc zVtY08)HIBC!_?>h9TZH(J?Mi;G?fpbZ?GrPfL6x(CN$7p=+YdE_3zQ=E~3xnZW^8| zir!Zhz1|2bV|z?}{*R^L%%-5{et{QoCEC%8@%o!sfci)1eP__kc^=)27i0S+&B8?T zqZ2EPW};+lFOS||4O8F$>&J#>Xou~v3U)#t9FGn-CDv!6GkOv$;&L?L1L!gP6zwNh z^DyJQ=<}JeUK!0m!{(fSH_Z?l9B3Z;CR>c2is#T7oI^L!dGw7}wMEz)P0@k7VoB_a z^>I?Xz6ISQ`_ZNS5e@tobi72%WQZ(V%g|9iG}5B6ULn?NqYpMk1L}y*usa$+Z}hkg z#zHs}UD9c2s+XcmxhA?9&A^@{1<&svI28Ym-g;e#_z=2lKSm?|84d6cw8QjPA&`9N z^@6cpG}cR^0hUKITPxZW4J>(myrFw^06LT5Xv)T*GaZkneli-s9JIrwv3+f9e*?XL zA3DIHc>PoK6#W?e2gy(}k-K&1xD-0mI_S(gqQ|8#`ssHEx|WmT^(E+OSdRv_E4F`v z2L2s7k)O~EoJWssmNp^4LYVsbzZeDAstmfu4bcwUqBHCr9fD=4{|}wXQ|L_Bq62M+ z^=(*&`VKUZZ_!h67G3HRZPOCzSO$yw`Cpzwb!>&c+3rCDnS%!K3_64LvA!i_5Q@{UfMZpKVpab?n*KQ~}v(fSTc(jA5=za6i z09K;+zl7eu1)cFbvHedpQ~Z%fYDu%A&tK7=^KT?2;)QC_YtezOkM%z21H;i7-HYyt z>FD!I(9eps=o-I??wupiuhHjzMf=U#AzZ(*1LxlhrDH=~bfD`n6T6@tjz)h1ni{XK zMxWb=uH`N?gL}}AtXc4D?5euB53NXqV3n9Gj4{?@CI}u{m}{Bfo5uQtj|L4Pp+ik0~^s@{SJELk$A&b z=-U2*4s-$QVEPSV#`U8u(CZ!1wZ0MUuNOL@VQ4^iMJI&o$;9+fNIZqkcx|k|5$lK0 zP4y+V$J1C1>vs;X;vs0qBhmZDqceLL4PY*ssnuvEUXJzMc%|R}KcwLC`vG0UtGk5F zRTd5C8Z-kPur}TporZoEyn((y-o?`R8Tunude`vh2&FNTdPl5;BhW9ki|}gC{~Hv> z;V~?W-MfVj#$zV+CFsC!VmI)bgAy^#rZeI57OY8%|jzwg$}R<-JA!bpQ8`{gs%B{bZN5n4)x2? z>qXF?2g=5Jb94`Mi0wVm=kM;#`FFQZqQSMF8C{6(+Lh>TejY30i?|(+p#v|tIV{yP z=ogT8uq}Rzld(phus1g1|ETZ9E7JHL(Kmb++?ebaHqlLJMhV$^^M2`l8GS{d~u9MBN~UU)dV!+ zY3Kv<(E*>2*Iz>K-;OTD4m2ZsWBmvk&Ml<*s`U3mXy5~RNpm3oe`uSf3?Vtg= z`&*)cbwg*`7yXud8@k3z(Ey%BzkIGkC$Jj_;YXN>4Q~y{^=9;RJcz09|H~-&z;oz; zub~0#j(&iZsh>cPXU@Ulhs@&Wo*9A$_!PQnSD{Pu3YxjiXrOOKccV*lXfWsB8GS~B zGdP9U<7q5|*W4CT+&?-TOVd6U9bhp!@UrOhvHcY^)tk}H_&U0W4#xVI=nLuB+mhj2 zmmU&k+zHJCVIMRWq&(7^Yi0soE$oa+v+ zbN=d6C`Usp^!VI~z7eNlQ{0CRkn7H{sq$fE>IJa^wnVSriDvR{H1H%E&_ie@X2IJCCCMEkiT89_{B< zH1+SGnf?fq7QUfiDt|*CxQKR`ZDe@S6hx2D4QOTtqTgm$p=muJ#IgvGy5&J zr`;Xum!tO;K_^%domhidZ-%J}-Oc&;MKO>DkJ%9Poj)Pg=b|Zn0o@yK;!SuIO?~bE zh5MVL^`7W}gU~e|jRrUc-6ONmz~`b%u`o%&7sLx_0I#4+u@7DIBhfF=P4y!h@GqE- zf5z*7qXTCh6PE6ZXc=^%x>y3+U~wE8uP3KbXiCE(G$qH;RR4sgF!P@91*9q(U<-7h zo6ze+&^<5)-7^cK&!P8iMf=^02L2hQmJpd>GVwD7Z@dsMjKX(iH8e zJ(}7Z&|}yK{r$niSQVF|&mBZFcMMYlqvQRYlJoZ;1#i6U-f%-YOu zebIqOpdY6T(C1!3cl{1DljqO@OO6Zu)IjUk;w7H{>nXSgZnOdWq7e^9_re{~d(aF_ zLIau++ZUi~yBs^Pi68-rg%l({xQ`v%oscC}_*d3i|e{}8c zLO0C==s+tl6L+BjeTyydmuTJb;Rl&<=tP#GUt*s{C%PS-z^?I}e{cAJ29L*4wEhK} z%5Tv?evS1k6GD3)G@v5UMra^6$LqI6N23`{qWwOM-uD=KJQqyh{5#_{H0aCd`P`01 zxC33Q186{>#rpSXM}ML3hm6=yLA1RDI$-5kuZ3p#S~R2n-ogI*B`KJiVd#t|VtHJO zrg{(hTdyCn6lR|oK76X+XzIPtKo6kj{1_V088mZglftph7cGx|+FpwdFnJRNQ}F~g z!{yinzrmJRW^!;SI)nAm?dVMQp%XZP&gfV4WA;DvI2M=^_Chr@(C+Ax3`FivCWeJV z;vRH>De;EK(T*2H*G1oseu8eMGw5#q1D$c52SVmrp__XMx`cP20p5=;(NwJI=l>HF z{IPgfydlej;lVs;O0PsCE{6_S4ZC4|w4+DSj^|@lT!DVNeSi*}{h{z7b``qGJD^M2 z#X93BMpCfjG3X4EXbNYc9jr$8!X|X7KEY!69l8XUJRAZpjBeuMXuuWFeruqay#{^0 z4SHWsOx{4@77CT|W$cV!;PqH@YUpSpdMc)(9nOe88GSmsI{IRCGkOZ%LYHDsY(Icy zsee3`^Y4X=G?(rr*W)Y#Onvq zr9Fl|cRIHJnWW$vW=s!XFbbd@RY%vVIl2eBL- z2J|J`-_Nn0{EvdE%snIgBC-IMr#=F`aX$JLZ8f?SpQHDG8|yz~E9!rt8Ef)rm_P@# z-Z$2Vp~rbV`uuF9zhq)jC?uB03(uo#wh^7#>uBos#_PwTU!yZRgJ$XidSA}RLI6e3 z2~q>(D^nMrX1gP3cjzj52YvAt>UUx-d><=e*4g2mUIQJV9eRwq zp_{lD`eM2xw$DJn8O_GBxDI{p2s+WvXLJ6I@auTtC$yvUXnU?F!UF}dF7*=8p6K0(Sg1}`}-Z6V#eI?r{^v4aq5$j6vk4>KQBBu0}W^inzB`B zNBhw?HKj273h6eqkE;K7~V%Sa2kC<{e~__6i*QOjgqbg{`jnM#FM?1yqJ!1W~=m>P?_n@2XLG<})=)iN({+Fh%bN*IQ@D=$y z8ps=H2k+rEcog0JS1tLD`*n25-a>Mvf1wl1vN%jIFQ)$d zuQ-K@T&NW7j?QQjx&+hFju)Ysc>&Ad7Iem6pwIspE%0;*v>AH6b+ilmov;tO*Ty`} z`8TrhH29);B;L3Z+f#oL{c-#sbnUNL5*{pq&bSmBP|avl^!Rl|ColjVXc&4bMxjf0 z9~$tpOOoMtKOfNG&-WM58?Rg%9xR3iQU=}iccHs`3O2{(Xl71D|3nAAbXoWrFdtfP zjt0^J4Y(`16a$hJoY|dds>Yx*n1psb2OW52tiOr|xI6kj+VRnN{bX$a1s&)DnyKv1 zggtN-x|d30Sxh#g;3gS@c60~&;N9rbOpZPluP=<(m&Nw=(XH|NyXedg;B|NmonVRO zVN+K?uQx%)O(r_ThR$fJ`l165K?AxwdLNph$FoJs*BfxEvj*KAMrH z=z!PZPV9^|u;2^f6tu;Ms82!m7~lWahEz4i_FR~Zeij_S^7sQz$NcNk5({w&`mub? z`f&Y!=m1ZnU)2s_Wz7C!_#0Jqu^IJcI0wJNsyOx~e~HccUroVx`d8?;*NhF}hLY${ zKAq81@d&yZH)ATWSkM1*_yt5ue2nW;@IlPCF)h&_=cCV^M+dI>O8D*gO<0rh6OU2w z(`W})z|&X)3v3E6jQZGu`n|Ef9ld@LOJU}#;RlPBXeMsM?zjNG?`JHF`CbdZA8ddl zs6T|sE)@QtFbLaj4qrysVQcE;wuB`af!4QTH_X@??(2y@_dec=b+?67KZU;WPGCd4 zYJ2!L-3^COpMwMO-|d|L(G&)}9(L~$Y)btC4#!4sgjBCVkKJMP+wwO!3H!boGWG?w zqMl<%__@C`nt|oG0{_MvaLHTYh4wT0DR{}-$?&2n{dRbxU5lphW_&P>r9pr2xMF8W zVSBuj`Y81C|0KFcO79AR+>FhrKZH$iH+IBJ-%U&0jy=%Ru>(CV`;!!WK^%@AN8enh z&^Od+%!_}bA2Qi@hxS6~eWfu2E5`P!=rL^&ZH1Rn?~2}k3);^R^nH*VOTn*Lk76dS zM1Om=EBX&sqF!@Puy1r4I)hE<8*M8(v$xO}%mFlz1lZ$e*Ge?}Ag!qR3(18s;oJpZj@ zLzh$on+i?o?dYf69kD(gyHcNt9-ptU3U+-jto5A_jQ7LLa$#laCD2XT5zWkCbn^^DAN(J>t0$p>&O!%XhTgXh z&*N)o{~Hd6jBQ1q-+^X&|6$Jm1PWi%kd6aC2saFlj*3o*PQw=5Hx~``C>r1;ABH`X z3w;sgLEC$v0o{xaJQ&>@qhfu0l7cCEBwlzNUF)af4I9um+3V=qUwI^4uZW)Krs!sB zg)Tu)w7?}GWBqw_ zhA*OlzKLdNFB;J2=>318dn55lxIZ^~Ur{XM`L9W#A`Ly!O)&-Cd<)SBH=wC~1ATMt zM?3feUDMyuK+--9naPXROQQGH#PZl0?SCX1;KNwZ^S_jW9lwSKvM>4v)}>zJXy~vf z+Tjqiqw!cB=b$g5o#?6fHC|6Y7BWyMS^^EE0(xH;Ogiv@c;RkzQ!GXYT81vkCiK3K z&=<=0XlgGx9=`cpj@CP3P3(@&@DX$`J%cXk^U)2MNqzJ2`1Ai!8phCY3io24&(abf zWBC)|#gzCwdN58D@h}S~+ZyY;&>8PXXY?tW`qSts_zk`PBD$2>zYN!NqZ7LleZDlBp=5Om zrmO+lLF;H|^nu>!S`I~DOjFQ-7oe$p0eu0ziDv9@tbY~#9X$<~d=3_?pX(FbZ$&dU8ohsVY@dnVzZjj!D)hM* zZ1?hHKLi@WO4WvK1ba$ZdjVb6v z7Glyhe3pV=7`CD3`%`r0f1;_)bt*)B720to`dlrvqt-q1JF$~1by&MG(%(14jw=Qn2DbE6=)#O#`cZqn(smT z`2v0J6nbCs90lKK|DsE9)%Rhb8tA5KfM%vGmc(A@nvO?1cqrCqqXR5LC$bEk*o)CE zXa?Rv19%_FU@~!>f;0FT?J(;P;l{jZhsDqVOQR1~K{HezUBf1^y=Al$I#4e(@Y~S$ z!r0h84IO75rvCfC<*5R{YKa$KjlPZU_JinJe~AY02O3b?k71@c(CdZJiBv&1b#pYM zz0gdKMl&)c`Y7h|{Liz1E6~VZLf3dZI?z6JZ9hb3{s}ts&(V&*M(_U(y)XTAxSkEo zU{18(yy!SZ(LgF<>hJ&7reMd7qb<<~+M@&YKm)lMeel*;9}(+gqm$7AW}pErKm%MI z+h2|C@1T3+@M+G!H=c|){)DFZzgW+4CUjUBy{{Y^SRJ(EX0g398fZUs#)Hra4MqFA z3tgi7(IuLN_OtQ~=ikChG#L4I^qB3%RD@{9C(({hp@E!>*Z)JGyX( z4bb~q#`ex=|GkqG3}7gF!GYGG9lRQEcs;i7LkId4Q!~QUjL_%*LMQSsI)O`m3%^gu zjrNxZZ7&$>MbS-OIvE=ppdBM+fYM-ZumtU@RKoBhkmv zJ+c7J#4~8-)*yi-6R%LP!>#B{ccU{sh7Rx(+VQ`!e%T*kFBCzqSBf^kCe&NwID8oW zC3O0q;TM;Uum<%ZSP>Uu9nb$x3gv0IfSs}QU*T6SW3WE;{a6pPoe#eSzZRQPe+d27 z{BHDXEJZ!-@9=B5GU#u=ugBZ)5p0U*@D{9jA^!gVNeeXWMmN_5H1cf!gr9z!qJd07 z1Kfs{@v?uzfDO@~9s8j(n}E-xkz%Ywy~BSYV`H%?^@p(!?#AS33T64-*FCrp-7NXi z(o-E3LD#-Dx`utx&2?LBACE589CS^eMVD|RzJ>4NgE%ccJux2hWTdD5T<|fhOZ|t8 z^l<)5WJyo`TCEZe8XFVuS;|>-p_sCVKclndon%suUxLq9&XH%BR%zpP@T|Sya)|sIl3hO;W zXL{=Qd}HuB>f6ykGjgS;e#P1WUHggX-q{xY4O>yKoI8v&I!U1{4R4^Q;1s$CevMv4 z&ujL}(o=7;%h9FCkKSJdU9t-3bG2gq+E{Oco|3NU@g0DE-M<&pF*zp|=Ap03g~;AX zJdK{$rRb}7b#xOt(9U@M5c*m0F*?xq=pOhJ&0vnpLqGY@`wF4`RYLAhCa$62=4peb ztQ$JxS!f4y(M|R=`X1PT9=o^E%VS^khueqI{#Ih9=YKN= zQ*snNKEI>8JTq@-uZwor8H?kPSf3i}OVF9Ufe!dS7Q(a9OY^0tj$KJCN&7%FkcY4s z<0n>7Frv57SLw%SK;Opp^!%Z{0NP#_{qSgp2Gkw>L1PRW$V{}KWoRHTp#$!V*T2WB zsb9dPFNlIygaBHg5B7-l+t7~hLsLHo9bhGz@>kIr?Tz&_(ZrRemQ9L2UmJb?dUQ$q zq7xZ>CFkE8Ceh&WT8P)+hu8=&FA$DT2ekeG+QAMqfIVnH$I(-87R}6G=qAfuFf3&e zEJwX2x|F@p&3#uv&cDZLEDd%r2}j^`wEY5l%(7e+j!#~6sVZQ1Y>Wmp2OaP!G=S&P ziM@#i`YCq6(^vr;6bknbOj0;R!zet6EeeM<%YSvS3L0^1Jb-=C0IL*9PyK87i_t(Y zD;iEsL3AQT(TtVDHaG|kU@g|gzp*AJYZME+a}c^lD{(7sLT5Irc<68(`i0{G^!z`9 zci|lDjoC888}t^ullod5ibYC9fRCu5iIzSDyy#*Rj ze=LJ{p{HaXy4I_)Hg3c6cn+Il;nE?X-sl8Jq4!Tk`(2Egp8qWr9Pk(#*%>tQf6y7{ zC=+H>6n#_HL(gvubcy<*o9#BNiWBjET#xo!xoimFTD0G8Xhv_r)W82VoPsla0DVzB ziGEnTj1IIf*1wI{|3klU6fGC-YlPm{E!Ib($7?E^FQH4c9sSh&upH;#%~QC1 z*i>WC6iq@iF&ACSmFO;i1D)}1w4+04px>gK@;~(BIY))?G29m2wBymwl*u>@r=rLD zw+hK{Lt4dPHniiMXv8Ja0jpwbtcwOX0S)L0bf!bmtN*J&TI#6w_gsrd%jzTlAA-V;9p}d9OpR;O6WzlFwbm{7$o2(W3{0(R(dSY9A z8XNlg{~d*HG?cCucJIA-GxcY&E@rJB*0ND_Bvz+=1&+caXoqcTq^JHd+Syox`ZqWk z3)W0e{nd+w=-$XuD|`iOfusEVpGLv8`4^p8^V(s6>CtW2mG(1u12(G@-u<)Dwf-Jc zn+e@BX?4SlbEBL0O7wVEL%*=JK_@sIQ@{V;L%|ylV+A~o?Xf_;(9tdES`S6negb;` zOf;Z1=u-WS?xkz$hvz$^OSBl>tn0A>zJ?z2UoiFOe|Z{&Jx~hW#TC&9o1!!8f_`52 zLkAd#2K0DzC3^p6^aZpJJ@QMqVJ=-`#UtSzi=StXdF(%o#_2j8gu^bXdw*_v>aW- zHR#M=iN1xUsUO77_#1jmTU{HTza9;wJNjH-bV9?>OpQYWdJGNdY4kY1n55tv>}_;S zkD(9zjy{mRNr<=*8gW&$gX_@1dqn%A0o;xTa2MW)6XNv`?p#hG;J8%}dt1qGh7Hl4#FO3FP9i2b}G!t#n&D#@wFWipo zDgORfyzm%0&=Pc$yo5L5so37MMHrw9wxYc++VM*CQ*9#};9KZsJs$lN{cy_HG6Y-| zy{|Q9dj1Dc@P^6phNscv@haN!5pW&O+CG zVXUvk)ZhPq)dCmxqc4=N(2mZcDZHdrNL@iR(DLXPh?eN}0rC1>=*%a_>x{+B=Psc4=W7%0 zFNN0Y#Cj|Axt?t}{|-Eo22=b9+QED@BkR$%+KFze&#?`Dhi0lq+py~!p#ii+2kI8< zx5oPa(0(66kL_Y~iC45uhLpZVgU9DYyx~;5;SAbg&URrh6hS+vh^YW#dn5D|v_n(e zBVHeXuK92@z+`Nn8S4ve@WJQM2w%lYcmUmef1|0ow0%fN@HkzTX=m58)pY!*l zZ^*~73x151v0{g?H+rBG8ijr{N=~6*#8YF#Y%~+|qD#=FSsmSkW?*OZJ#>?O5dA#* zBRYXUqgk(yn+{#dOk@uw6BQ^pgT|pDaRWNzL9u>6x<_Wk`WkFXeLMPoxQIUAvtxK} z7<&H{EQe2_GkpgQXb+n4k5h8~zoyWghI8mKY11kG42btpzkr!|&kZ5+`DhB4p=-JU z&A?me5*|PU`UYL1ztAPPtaI4R4Kb7YjaZNI6JscNZr7n5>_N}@7w9hi2R#LayQHUn z4_F(EQ@;Z{;G?mAG}iydGPJkunx6U#$#rXy}kxp^fNo;VKe_XDKAWa1=+3N-wR-dMD6h_nnEP))3l4X`neLchnqh&AvhybVkA3->>Q zji}E;_tbtg^&g>|@(eojM1QYy{&G@qCRd=Z)<)=g?Tw~vD%!!5SQlTw>+vhR1#1il zDV>9!_ZQKL>_TUH0Nn$}qko_uVtH=iH?E%ln<%*3`(S4r9_z28OS2#A;cw`ssXQHim@%MhQ3GD+0g_$qIYp9>VI#}q|@Z0WAnEGtM*0g_&sek{k_~7uX*U>nY3+vIZ z%k^&yzg~X|&CHMJlH?c?Hf3IPrdOkzsua5Wo1nYD6Z!%hgl1+k`eK?N+g~5T`S)Fa zoCc54Z)nPM-yXh>7e*h3|I}*)QI(2)oj-1|8^MbfBzvgbWl$>vhpzEVM_D@!+kfQ z6P%5Hc&$OMClh-pxJ%#1{&>lVFwhWmvy4YGv^=)2MQ8FBnt|QudHyigKacevWBm_w z3DQS~fQm-TU|!FE6$*CL1ii66I+MZZ%*LUcX9~JEo<#S+D$Kx-@BuuEQ?TD%VW6+j zcm0pCe(9(%P9F4>6v5QL|5w=p4K<>5(aq5i4Xio37doO3_Kx*o=nH0S^dU48bI{Z; zLHk{eF5L_0{X3!uG3kZRV#6<(dNqy?DbI=S{wvXAQzO>9#rjC}d_RHZ@g;O^kD|N% zZ!CrR?hY@ey6F9*u^c{rH|O6JZKk0FeuPH;7drFA|AM*Dj;}&zR3FVyV|0(SMBfiX z(O2yhG*gSw-M$v9;G5`#&!XdGjN$zI>diSOzHrbFmrQic`=K8uccE*&4h?JrI+NG2 z9v(+Cbj3X(gZ0pUo1-c3j0V&X-9sbLekLXRr}4$h+; zbWbHmQSgBzdd#NcH8>mH3;WTOe}WGD8QRfV zw1bOi;5qLN87qWNpb~ojwdnnw(17}(ujadv=aY$P6ioFzOwAnaa9eEOi#~8XUjGH{ zFzdMR+?8ks%b@|)MVF!lI^bZ;#QWm)r_k}%Ap4)cctF9FypEl4FIK_A_l3{-HrSf_ zTr?vma5sLB<8Z_MVX3N&4|}K?x;I+m4R|a1+$MA)JJ0|=#?pF~f~_vq68j4pL0Hh=2>|F;eWZ)k$4 z4+%8F4rm8`G4)1_jzb4}3=LpetZ#_!MDPCu9q2R;#6Qr})oWrHZ^1;)ziYje25(%4 zuH|d!8o!G!&By2q<*Qh~fM(>9N#UDKF*L>f(M%3QConO#&qimy7#rXxu|3~pA}UNn z(aGT_ocidSY!KGON3kNl8S6jBda)^CQ?I2aC$0OJjcVTtB zT$6aBCaI-_Ut7W@)T^>tIj zZ^zf5o4Uv&;fLJe=o|NKtcJ-iD72)IXIk(^Y({+{Hpb)F91Bkm-w*nrseTDf^|t8y z=qvgZW@5G(VYioyHb$4YD|$@(AQMR@hK537GP-69(Ov%n`oIBnk9>k2t5dO_<1K!)Jv7UV1!kRy6Q;WBpTfbAE?q@joP#Kugn4HfcM1f520_udFXvFq4(`Z`#FKm@E3I8ztD-K%?rn~0UFpX z^Em$o@IM;7@qTm@Js6!8U4-7Z3hUxpbbznX)c$~e)ATmc^RXtDxT> z`eRqT7tKg=KLsPqx+r|t%a3lxYUnwwhwg=zXkgvZl;4V}jG+TQh5jP*y*z3>DY`3iJxx1(>o_pvqRTpR-Kimqwz=$&Y0 zCZT~(kL?T5SMXX){r5j7D7XaQqXYhgepn=)4i7d&-c*Tp(Ob~-JTBH}q8V8o>#s*Y zLifa3G_ZV2LZD^P%vMp)|1}i6p*1?gUTDhiL*4Taso#6>|Pnr`Nk!8nZkT0;qsxsW(H{ zbO`z>Ivd?PYtg;&3L5z9vHd-CNj^mTKk*Fb-_)O@!Ib=o&LGS3FoQf;pL!-5@lELQ zy9eEL>#!!ig1!&F!ycHvBK+xBFSP#`(PO#^ufwhAr*7KHWQe%l$}saL=nOidfpkYd zE^k54YZ6WEG;{_F(LJ*^w!e-p#evxVS*)K$@BbHvVV+gtzWb9DT=VJZE?>HpO0O|BJ8{uE!hk53G&J4$r2i{&|f_Xlin= z34eIp728o?g1)hSK=(qm=fbzz;n-2;8mQ!*A!`7CtrEXUEf16|rW z>)f=Qzh)F{xEWo$A?Om^6YCG7n{6(-B#Y6XidUk~?TGdL=yTtr$M<|R=lalI6y5z* z(7@|qe$RhH3dONgbOieAwwdUfuS1X9+tDA;`}4jS{wTHv`l4BaUAzy?P=S}iH>ql9 zy))Y1P&BhiOqQqc1O-#J4c+Y@#v9I|n=R{xkb$ew%~cY;zZQDEJ$lXuL`Ou&qZyix zZsK|9Qa%%}Kfi(VUzLW!wuqI}CIYe9!J-;`gsqK!wCkDs*XmrV@pedgn+gG6X zZH(7q zSQx#p44UfN=;myT2G~Egk3#!>I7z`*=zJWGZ=qket8EJBbQI2_z8kCKO|OQ}?`dcN zC(wbuK?nXDooV*hf<@5@*1(c@Lv%E{G|9OXI#PHBhv7N&Jonig2AGd_xCGt3FQcj5 zj-H0SXds8trTP?|@#p9s`2&6avMr&#Bvz!}0Lw9cVh{z-=?qK_gl?vn(T;aSccUpj zi0*}>vHnB!cl1~#wuVe(K~tU|O?4Ucxw>dSt+0^izjJIDhNff;I`HIJe*}H-Ni@|f z(Nu4V^*vaN`U$Ly`L=~Wm}rZc)bB%=_$l;Fx(iof&g}%~`QJdnl)Zy~j2?^rf$rA4 zuZPo74l}8@z-BlIy}k-N;WliKx!wrxfu7iz`t#@n{*C5-Gfbo?Cf&_7DLC`?=rQbp zZko|p8E2zQ@&>y0`_a@Mjh>FzFQS>twIfU@f3#w>S+pnG&xjqIe2@V4_|4=v8tF;&n4OOGKhT+{?FuQ+iw;~0dt)`MgwxSXyc*l{yc+@=j-HDD#rnPI z^OIve`B*H>i!McX_Zl>i*U+VSE4mkbMIS~3&$>H&_~bxSUKLGk8#LuNqM7T9_BSZD z--&!IClmKj@PQ|>H9mv=@dxyQ_ItvI(v9fm8ifXQFZzO+gg(Cj-K49~rPzpW(w*q( z`WU_cEV@^+?e!FJ{t8n_Z5H&ww&;Vs&dD3rz(=nVFtsrwim;0tsQoJR-F{$8*cnxR@a6dPkLd=|a` zIQl9-jb^Oe{;>Ad(Eb}@>firrNx?Orheo_A*0;p^9&~1(p#%PgF2%)Y)&rsATllzXP0qkIBe*;}mqp3(x^Kplh}#wx2{Z^>=J9elT3Ghd$RS zIs^@561tR+p#d*NC%PKD;--U~e{aZfC_S+Qi(opQKvQ%QUHc!f9A)Smc=X|hJh=gfh6lua8otMOzeT4*RklEY64cr6=+~b z(9C>}&h$Gpz(3Jbl=VnxFOIfX#U|JY8{_@)`WEEFjz9mS;2!uIjqp!&?TdXB0;z)z z+z@TQF1Fu{ruY^#BSX=E?!gxL5Z1@NXuugChv#$Q1?r`6nCHLvCn3`5=x$wrrhYxT zH{Qfnco;ijp-;nqA#pn{re5Y~Nd4Prpr4~D{~q1ES04*|rxw~@Z*;c4?W&X(WTjlsek|X zEeZ{3IDnNg$7kV-Lp^lO9zkC$@1m)`fNr)+PlS$2qcg9G2HFVyIifwf$%e%1qhozM z8sO9uoPQsDng%0$7LD*Fbf!De4)&oPe}via8}!5HH2RI{9GbygpNEu}LYJ%|*1^fx z0bj$WnDvYB?Yh+$$&j+AXfSmzp#khd2Re!dauQA1w`kbG#lsguWoYL{ohh-84DA41pF!GgA}mVPmX||BLM} zpquzr^s`_$+TSNw1;0iol+5>4_{x=uuJMg%q>rEv&WtWWXR;n$vX9V!zCeGl_!Z4u zfv>|isETMNCZor62^#Qf^i*s>o=+zBQgEQp&=j6QZ_M&dSc1#ZdI5AZmOww%`k^yl zh^6o_x`+NiXZ9c7jJZ#RJu(zMEf1pYoA4^nfBtX7npQ>!YL3nDdbFdN=w4WiF42nU zCUj=I(ZCNyzeO{15#3XHzYF!U=;>*I_S*_m=f4{TZ|oBdqBo%f_m7T51DyB+=idvD(_jXcp{d*yZ`hB{`~*6a zb7%_xLqG3x{TK!=gAP~??XNDT0*UQi&;W0W^?~vF&>uPfrff{SFcqEYOms7?#`gFk z_Q8MfUhH)`-1i~62Tr4z_zit7<4iCY8t|3qQkFy~QWw3yWs-u&tt+M?L}xrO*2iEf zRhY^Y8qhNIzBA|q5@*Av&4<@f?S{TL7NP-c!c2S{E8y4Y5+*PEDWonxn)<>x18blG zy^g6}js|iFedV4+2mS{g=(3-~j4NUp>b0;Y-h>7)1KnE-V*O=gLdnFN6inG6bPYd6 z2mAtk;Jax0xp2J*+F_|!ua2g)9=a#4Lj&!LW^6>fo<#T9qiFxnVCwJxJr^5ZMjzOU z{cvCE2L532m#~>?qZw(6u3bBH32s6=9*zb)3C+kN^t0mCczu7o{$;%WkJows@UL8i zFAP<&GWGuGi|A2wrt{H`R-yxMjqXJU{uJG`XJb9vZ(*}t9j%6Ds0BKK&geIz-k5ar z&7q(R(E*p@@3i3|Z9Z&zu`S&<|M8h&Hc`-dP58uIIc=LbZ#@%Q~ zoa|J`b`eUxcEsx0Lcd9Tkor+{g311u zWTgHnh4;`Khg_PG@ayxd*aFWBeG;1D zthq8$?}^4ZnR+LD6c1xl&wu~iVW4F=gbU|z685?*BlRQMaU4zk>B}=x=R04XjD(-} zmC)nX2{Z9_tcKHYTN=NfLnm@izL3GKXaL`#nHiQpgAX~*-*yTOX~=p-M(T&cW;l@g zlXx4R$MHDe%8bbT`cUq~e|6R(Nwz0v1y$JF2d8$rSI zeK+RBY3Pe+F51B=^no{G`%d(Gz!7xQ{f?Eee8G&=-x2ACt*I|Ym*5-p8&lD%GE#qp z(-VF7KZ(g~6c$o&;AQCfUyHslwqQQojRtrudJYRxzobwIusHfzP#^saX&2iEpr3l< z&{ytb=)iNMiwb3g&;O+~IO7+w4Zev6lD%-S0Jf)I3Nvv87R6byzAn}eVg~JJup*v| z^&(eir2ZSwOq@ddG<0uVRwNlx)TKy9;$bhKA0GLNW~6=*SqjZiPqe)s`pO-NcJN@l zz6TBH5PEFSVmZuREY$0uA9CH$O!PtDkhdl&c)Vs~dHfhlV4`?9RwdDlw8hpq8y(;% zcEj^H3_E6q7tR3u7-`wF9bqXGJ2x)mLGCc5OwZ4{i@ZZzeeMo+~Xent1ff3ZDx z$*?C1qcf_4ZpOOU47+0)oR1FhI@_)u_-&trGwotxUF zJ<#2MGx~8n0u5jsI>1Bd?tUV+uSSpS%klbq=nLo5*nV@ljMOi${)e7|g;F!M)_blEKR*V`qgVBI`DMNz$MY;=#s2PKj&YJ?OV_ZypE=R4?5w`G3h|3 zC|rj*Duh68K-Y8>`r??4Zl3)(4*x{gcvQuZ%K4bOj_&S1u_ykA2H3MwXulZ^a1gpl z@2JH2cg-KB!5(E#8LG-y3 zXg@!pdnZeku&J-Ek_?-!0u83LA^JckbfEs|W*LK~{(1EH?ZQm_2E8v^)$kcoB-#@_ zu5-`{9!Cd0i@rItR?A5JFk3W9p%D$e(TL}xsacI?U`KQh-a!3*^uChSL%=oA=dVLk zJsG|J5E}54vHl|3?;GfG{uJ-TYtU=hcmC@(hM+aik<8~hfXRs8j;sG?} z|Dq|+drb(SEIO0M=#sTWUo0Ka8TUY!pnr4}djA8_>F8;hjRyYgHJpDV-W)HyhtBj% zbb#|{0BH?FdjWJ8S3xt=8ojSuynb_ZG`csYp?hjM8raKdpgYmP4m9Na8^947obh+) z1OK28nxy~+?ubS;_j}+i@UqKyHjX^BE^bJaCa;2?q1y8-Q6$lTrL;*|8_FDU)H<6S?hG4 zGqY#UobxN_gg+V1&2305k)X@zw|5Q*9$Ms+)@Cqp5pP(8_T*7^? z_=39jSwL;H45$q{8M490=z9EbVWI{u8sV9(e}L*dR%!Q9r2_S^%LS@X5l{un zTf8=?hT4ESv5ue$cQ=23P>=s%pz@}GN%j4IJ`+8$gn@eI+5_sEp9Xa;pMeteDdRqY zD4;HdFQ|B7Th|B03kLNt>j~;)#({p|5>O|3#{4%y*T4UH&qU8~5z4wxA{wZhBeh{} zP)Ao5)Xp1%D$p8Kfj*#iGzruO=7MTu4XA>fLG64$s1rJCc(p8#e|pBB`;k=w8ws7uk%&>xgeN5kHrbcTVtw4tCHUuPI*{=?;*?pHcDaQH&_4(jGe zQr^8T2x_N|K-~+$pb8HH)$nLgI}9~k2I|sm2G!7MP$zNS;`czEz$;JQbZv)p2@I&kH$0ok(R+FU?wky152}Dm2E{GeGGr0QDksv&DCSx&()9eG$|~ zoOf;V9Mq0JfO?$%0VNctqMMiwR72T8-5Z5K@v4A2;-;VqbOm)%gFxv`wDo+8uLq^O z$L)7I&NES`_dy-a6HrI?8kE2%P$%G1$^8tN98}>#pyHKn-3U~E5U5Mm4b-D*6sU&B zfhs&7)JbjvW9j?<2`1Xvbx=EbWQ1>^5@S|&C!_{dxB#e~Ry2QoP{N%+T~jBhOEL|V z?m|#HYe4Dm1$EC{l3$Pi`%Kb-A3*IqK@~S4IjBN@pzetRpoEKoYNQgV0*%cdVE(S4 zb~psoJu(Ya!Ns7i`36wmOLl@z1+Fnsp~s+vKY)5-@u}*50g)2a4vK;jt`6#{H~`d+ z2Y`CiOa^_yji7dV5p?}P1Bw^En!BOIpc?V5#^YbtFbfW0VGC3ObrN+zHQ3nvZ9$zt zS5QYj092t#pc+~R>fyH+bZrdOz4h4EFF-xBegUPX9i^49=$ z^ECq{+{@OJKsCG?)J?Y&)KMPJp6sRcH<<-a3mPF#mB-jok#*z(Y{HccAoqYPuVCCbCItPys(s zM^+rv64VJiG5<^Re+Jdy4^U6Rj@s_EFDSuGpf-{ZRHGF@HP9H;CG7&L;ZdOU zCxOxR_@B;1J6r^cxLE=46ez)KpdMZ?KoyEq$9)3vLEU72pc*R%sz6y#dR0OFRNM^A z06IZkn&lSX20B&X7!wKI044C+@H42p`X}hRYwNm?IvOZmGEfbr1(lx@)TJp4>e12& zRKaeb8X9ctDTa&c^7z+LZNyO)JPfV}Bh?G<`qHUUy^;IZZ8gAhtb2kb!QEg_ z@E`McZS4MK^mec){>)9>KMMwey2<8%<-z5Kuffu+b2km|`nX;Xun6l5pdT2`+06ai zpBK!Bqao-6uHaT~sxvl=j&l~i=&FLvAjZ2Y|Jg=eAM77DII`;w!v^s!FpQi;`c{Yq zqr>}Jj-{-3!Ewg1BI6(>;Vx=VkqC^5TzFsZ!tGXMI#`c{X<#$32JuKH`iAu~bYh@! z9^}=V%lK4=V;ZylG*^=OcIE>ZS>SO690Mcq_-lnD0|NZO;xgWAc3fnPr+}WhZqm#M zMi65?iL#>14;kMV>_fgS9sJE2M+4$*7)9~_MPLFOi-zV;H2=ZxvHAS}^N#g6>eAs- z2wak3|U`t4x+Wk3O#`rmH8iwZ#OM1 z*Xi*;iSCZk^>|At0AVzuudVn^1O{31G~?XBcfjTgnR{D9^KWQ9rUg=j=u7o)lI?vU$iZN$D2Uqwzvnhm62bu{#5Vru4LRxE`1 zKlt~v-iJmSa*Du7fqqK-gI&+RNERay4^m#7vKS=RWZuGdoth#kh!0`3M5qY-8wkCH z*Or(M+$Rjag}8pt->>e0I$IO` zAsm;5Gt7!`DN8tSA|lLUM^$Mclvo3DyR$xP8WD|miuDROSmz<*%I!$xySucUB zS68x*_@klM(e&kesq61g;TniUBe@Tx@f7L=CSj~(WG7*g*w{(0^3#x zL(#fSPI89FD$>kt))mmzp`163EYv&Yq{~SpttFV0Krfu%?TBKND62*CK6cU!3?*I` zKJQ06ev{h<#SQo_GMA+!XF5K9c5zu+{8Q2W3?~#He_Ft?i+DStcbGfB5;$Sqro)*W zr)(@myp0dO{AYC#ZO!_!6;WU$8nY4JhW|71m5k*&4fH>O*Q~~1a`qDI;?~#w&raC! z5C}OS4XFbfCn2i4+qbdAqR!q6g?G%R^PNH}@ z(^#kH|K4I@%gvddWL_mZ{-TkmH1vQXQNSEDk=Sm?f8nHe@4#`8SZg~qw-R?Y+>$J+ zQ*^P#ssx*!bnkJ& z+D;^p-;O&7-&@8Ogsam~OY-kBuLQP()5i)X;8OXaRoI#?O3@E+Jl2sI``2eSxM|o) zGLoBt{W%&w>g(_!u@TsXq#>3##FgZhIEgPm>sl-?QFI6E*{u2evdiA0BiqEuRb!mM z-x1ydbn94?s_zK4UA|$?uY+8c+psr@vJ)gkx4G7f5j$*6onU>4=C>hy-A-vU{9N$k zaSHpoq$fV<@`Dy6=DF|xvlieFnEhwt5bi=j*-GZT;^tE< zJUg-uWj!5kXNF$i%dXK_U9c0)aPkMaPKx?*>@mHaj-I>YuCEm= zX(8k+#Cju~meIyIBM@#1rJ)N|Ni z-$zuEc+1;-*@1I&)^H{#pjm$iZqnai%xXa(C@BVzpB8JC>`55u{@ zia)01SOYH>$S+x4_F1q0g6ZNk4*k5=i`^tLQ86wuZW233qU+B z8!$fu?=oYJ$k;15s~8i}SVW!uj1YIoQ5oI^e*WZ>Om0tJg0csQRiscycKnW`KFH`! ztPF)B;L~rkj<}dSXTS{*2aRT+Y@?cln}(vY^7z-XK1|ax{-&+tfyJ`wCG$u|Cb~I@ zSU7McBtF{zpDiM}mWg|u|FE8KzC9-PnBYG0rlK>3c{2Qeq4%5S{^0u;oeH4GTH<$3 zp_?j9>LJ#TF^qXTL`sT`K%iGbE7mV257iTwfRMl(~99d@uf4<4_2$4h9 z$TafSQ0xl1iam9(zTDhwuv^3j3x0ZV@{UE#lF-h@V-XN(X49zozU zNnOEitm`0NnQ@qeXsqMm-%Wu;JY$wdFpQWiK0@)WXc;&w+-^r7a`;22j*AETpt`QO){#+^uPFS&~me?p-e%pcNJ zBK*8R>9QEiXEV!)?gDCcbTzN@#|F8Ua)`f&!t=>rf?!X0 zornj9=Z?y4YA?Vv6i#d%%KZXiSyU39kRTfdN52!?g7`#63ktskH^B+Szl#D*yfl>4 zil*XC^Lga@{~jDQS*;}FK8kILhPQ16TZNHi@1$}{Td!pu8~%HSe(ky6{8C;+?npZ5 z%X~fa<8*P5_?65n-31Tg41TUe@ zTfq%vzMCVE6(GL|#oEB%${0%gGXBBDE3@to-oiJE1~R#{?eR9umCkn$oRbJ1MWhdf zZbOd3F4I{2GIRcdsmsDybMI+j6yhGc%Q`-JO>jI0vx99dK9JZz5z+3ze5YP-)kSnE z#51-F#bv`8_aVuqc@?NYaydo<8puGb8}q4*q3nJxSQR~g^gQ+#e%UfMHOcg}{=%^R zb1Sm=B(Il6KrEkE;RCEEQ*tz&tun#Ke9`a7QyoDT6UZQA&fi} z9m}Z6kX1ux7&*;ZZ=;dSjG6j-r`?#8Vw50I!t2-`Bfim&ZY_mNdKH&oCW^>nP<$8M z#N;kTXDI$_U|GAIiOAaruO7uOSi_}QXJ!4&$rssfx{pWFQt%&yy3$MxgyNaVeQRur ztq+s4i{{27z7f%x=&WO&0F7607vcK|=PR7PmZLhdUDWu)$j5M9|7s9kk|_IvQ>OpO zc`ftv#PTv932vpK9?aY0ze>S(h&N>|n@!P4#Dnq810$G@Vi$>B(*&zdUN39thUyoj z@JI+<5LyCy+ZGm05qg5ZsWs>wUyVO0-~8W#`6;{%EY8W*u%@!n$Z34b8L~nYA8o~Z za9TO|_a2UfkP|@cN}&^A0s;~6y<#r=$v8(+6nrg-O`}j&d{1a-59>8nOxzEwCsCxF z6`aF7A`Pa3%Ue7D*&mv0qDDNAzeR}lz|k0iBqlfr(s#ygd~wvEoj@75cWAa5`TQ+V zmsNo?lwyhTw?t^Jzt zYnKfXlD)=X4}Um{$v#s2B=b?s2cflw;cwi3(cg*xF5?t_KX@th{kAa^-pz6}g18#7 z+sq@-#8$*SRulhJD>4tE6>wx5X!HXZg+@l;%fUfQAeR5aYrb@Ct zY;%3y-w};sxgq8w(3)gf5Ml*Lx@~LE!*mmzB5sQ7>uie5BsV7wqyYIho-P|{4HmHc zRmAcTnMC1D%u|y$gxGs03vbIqftUEQBlwtkDClizt@FXeWD^kCitjaoBbj$$%p+Ea z6Szkl_kVT`e+^dG!LjCb&NRWu)<_RB5<<9w&^U^hL8!MiQ3RjtZ{jH#O~{=Nrv0By z+~5?3ajG60iRKSRVss)|z0sV|X#Ib8DG?n)&|@V?T!p}TL^GQ|GxLV{S|c!-@si|f z*351aciFLrBX^4(y$=t&;S_sn8m(wF7dZpTPY&-c8+)h6|5BXs5P1$cIi#T`riOYz zmSs0zB{&V3H@D(r;XClXp=bvL3xKsPXCk2xeA(d^_d21AG}MUodYX8q$A4J}XK?01 zxC9BW@O?ytx7Qt084p?iv4WT2{9^t=g|UCZ%rx-Uy>#qB)MMwY=oL8g;G`ojF?!D! zx8NNEoy{rSkzgFKf@LPK9R$(LQG6l9WP8CAaCah{4bl3<^@h9;^96|cxmf&qi$Wpr zJ~Lzoi1#EX1(?aSKEY|F*ME5-MI?EGo9oyLX|^4=Tt^_L;9ANm()AT^GW-wZ`BHp3 zO%y`3Bn1wUE2~R>FV(n$B;?3ifVI$=!SG=|7#u)+ z5MwjkCf0ly+?VbePRCXPvN4d~Gpf+>1AIrViF_1)j#zufI1^}t;1Rf+#j*S$tYb63 z&X}wic`XyH5j>dWz8m-|)){BC!3|VIab-)=U#%3;Sg!nn)dGY<^ zrkV#vx27tymd&vp&4nA8*e5sxh*hOXMB~IK77xt?jJq^Z!u-yQOjcS02N0TPySol{ zfH(`xNz!-(WUmmHwZ?aYUFF1I6I_LF1+n({N3gRa_)?R*4gD1OWYMk8dE$HBE~n!P z#7MS-mK5AipuZ8t>8)KM(g5-Jj8V+vk~0dfEH?3+2%aHlHU0<8-@|#q`XdcYF|EhM z%1|dF&6Fb^;@S|`|8EH432cJUh52xTvQl7VNM|UzSV{OAF{&{Bb|v#tog7&W+koZ^ ziIr#li}@#TFgoe%1d8FSMB{15y9e*M9!53jcsrym%-1o0#5^X6-63`(F$aEGCg!V& zWgxjbO;ut3ka19kM1SbwJhf&O?+THf&0;k z;CJh42O^bdW*WqIjGIz3+YRp$%{>BB5j%@;6bg7O(2jmNyFSJ|gKZ=Oyw>O+g4h(y zMUCV%+>_i<=uC_7D2{U=&UMyAJ)C|JysZQbAsCB7XBp$jD~5lOcLMW2 z_yfSzH1Yx7Fgv-%aL3@wN0Wu%B<0`yI|eZMhNCv!%BE1zWBF|-dqF2%Z-6t}#EOxV z&+8Nlqm$71mnbA_ftDx%U@l_mz>NK>56-&W9H_l~nGBBp&d!zuC4u3W@Qh}dLdkE`1Xhqd~F3TwHu~BqB z!3uV^oqA#(oIuV-6Z!$S8^XR6O-#cf%alVYQmdorYq|v zU~GJeDfEY&2XJNQi6vr$v$fmH^&fy!)|Z4(+oeAZl*GS-g0o53k6;Tcv;v-Nyr(cZ z`Po1rcD>LpOGDd08k#9itS4IS(8=gdb!5QjR4wYeEE0}wRy;4BZzfO+!Rf?WF=S7z zcv3K%ao)mNg?L5s6T$mv8`?ziag6Tpe$ebmuq~XO;5f8m!gZ#WD#1RC>^eHcWOoA^f1>f3fl_w`1{)w#X`>_9L zBxEE(uZCsyS{TnWmj)}~&tmI2?EDYS z#G-Iae4mK5!50;cc9uV$bsE+YoRGdj_E>x-BS^Yr#H4hZ*b_92LGc*m6#-Kq(wNa4 z{#A5V*r|C^M&N4$uL(MT;~Wkz2AnzM$ejJ?J~qKTjBSXPXXIcMhENf5U6Q?R4$b92 z;EXlBAKu@L!6qo~b2vNDn}=^c^E%d4C+5SMU#4b6H{X5#C-<(vyflP=AzY`C1|$@< zcx@WmZw*C9%wxqk&O;QG`4V3RuM_k9G_ zPtsZNCgjJ&Ca@mj*5jn%A|#8-yq*bGvz@KQp9!6awyp}NKgGja!)J*9w%Aj&M|-|Y zJS1tgk=B4$jHE&{D6$Jo$u7&nE6n^mqCSkT)=Xsl*=V$ot)~)82tNwXjf=@2Mrl84qVN~Scf{J#z-$N}`$a+r7K3cZ8xVg5=Ng=x_@*&#qHzK7 zqi|)j@YO}@DBMS!@E33k>ub!H!M&vVXROEq5>rt8E@K45UWiPF6o(==Nbt9vrFCn& zzRpLiG%?w4Mn7^#;(JQI592T+ro}f{zKhJ4^u%2IUq_*{u7x|)*bT0)6KyA>*-3RT zPIBh6i2b6$m&^|mA87H%6wJXoE*ndRFB9V~+~iz}wAPHoi@{0lq~jTkeRQ40ih1gn zBC#3%GIYKk&OH)-;mgK43+r1D?;^OF6Iuawf?JG+rhxb1_JMQ2G~429NkbXHndC1) zCo^%^_5VXsDi-|_41-YHI@mg39=L!F&E2yGF zZMTc8@pS*QfjwxaqedclCq1u?YCwukp*{%pVqQ;5*h0E&L_yhSMt@=nD1O{)XFu@; zGLkcv5!;AHGCSf!hOsDmocscCE-^CDfXsFMSK+8;jwx=x<2v2-z_)?o;Z39nyaKEf z+ir(jW4$e&k$4>PXH(2$72qUgW0hz)19_FL&K@>~IUU(eWFv&Zc-j#t!zfGAFa%}Y z;XG&UYlYHq&&YP+JAp4Zyv~du=8Nz(r%-(6@8J|DcM0>U_`g{#{WubyQBUs_rL)~g zuq&ejWZ5hdWVh%z72-YYy52GWSQ-w`2tsr*JMPCkCu0fg%B(kn$*o{4uNsTtG-iCI z&Ncm}=oSlEJ21S7zoXzM_YpZZG7rxv1ou364DL;Y%d(!yIu&`nY4RZrb|>cr8l&)g zTT;`SPwXqaOXSL&wdw3K-R?HWbADFLm1W&WxfWM7bu^Fs= zSnoxzBN`stVVv}6{bK%t8k_Y_;b|so5#2;!AxUFc|DvG^B>RDtP{_on3#Sg8rxZ?r ze;VAYaAgVc$Fs)LBfie&?kF$(t++1@KZLuNbs98I>R)DbL2y5dyvjlij7C0mI)nTof(oW2Y3PDi-=T)8_OO6@4TAJhj?UiVi2o@?mco&d@3BIr z866#G13Hz(2df}{n$8B%z&_SZ83~wQrbrTENf4dO`ftQ0Gw%yG6Cx8$Lps};mjDC6 zmBc#OX3nzCO5p|8&^>bZ>Gj_m0?9}igQGUV0j&Eme@a2wMT&(Xv|1;?xXnC^JLHJM zJeWoTXmAx{3uBG#^bR_gh`+#B7u_=Cq(XNc^Jm8C7s>Vf$1heO$}(EfCv?34;n*DI zJ0l-peS~=rny86rRfenxqTcqL^%i)gh&Q8&qzrHSr5e;r#`vP={|N}jp{r$9NaS;@ z4{~qu06KRGx5Tc%sY_$6Op0_ zeqhHln9F{$p2z$RjSNy8?Yrp3g8vxb9O|sWH^%rga1vnz=%V_g`*$}D;^pXc$j zj^w)5TU)$M6JzW6A8F#n_Vw@7JCv z`S$Yf)HBf6uYX{0z`)GDoqP5O@ogRG+m5u5!0x^w?frvUw+jjm_9nYL12g9c?cdNR zRam)(J`0+MCfn|lGW6FrpCqA=xA`OwE4bZfZGvdNLBYth^=}gxcITAO-(HpJQ31vg@6 z%=Tv@(Gsu5-Z&q7;ZHb@@e_?tF*pq?a3r3g};UK8>C5 zOEj=6&V>4KG_ciZK(AvhO#Gcl)P~y1_=)Q&*wLfWPtlpB{S#7OHQEbppNehpd2}g` zV0kS6Z&;$PXsY|*r8pR~;z-QEo6se?6_c*bR0`fWFS-C->!)IUMXayIEVRFk`SCq0 zgkNAWJc{n2EN59RER9956`G0um>Y+n|*9)P~mqRmFGqyL1?Hyx#&)A+E7%z;*d^|7-9r&);z6kB;*?9dWG-Dg% z^_{VOKNhC_S9E6S7eYXJ(RxWV(5h(an}_yfqB8~8qz@Lw8)AJrdgJ}*fRCery^IF* zHoB`nLNoVi^Z+)d{s%U|su#%u4nyk;uo`}Xmw5iqP%yO@&;fEM(o!kB0`0gGI#5$| zCaux6y$a3P&GGt7^c3BVF2Tb%7@vyubLi&GmX?;vL>|n}_=!vkMqC-~;CeKmap=sa z#rk|SprzOf*I@}fg=Q*WdRk%umcVE5RlE$Z&ItVskKT?;Xnz2cJtWlLlBi9}g+=I^5YZbjcCJF=xE zQz`v|hFfU(9(!QVOVU!W)aB@h%W)il-Lt2q_R34qx6q7jiGCj2_oGX7B(|SMGjS1} z&}EmVrIz}NOOv5cJvKCtUWq<%ZS+>O!@JR?Ss3fjp__LNx>WC=OR+m%KY;F$U(wTX zKGqB7NK3sT%Oxq;VOw-Z*-85s+fNw@W1*c(O+>8ctd7hA= z(r5L# z>(GI3K(F751~wI&;9NAIcQK3Se-8ya-j6=`L#&^Sp23#1{}*kZFJ$CSbT8bCuKiQ7 zz8*bhAE6oj6wTNVX#dC2fX-pk3t97r4)aG#Vk6qCVimk0wm%eo3f-hD(GEXF2i%AD zcMyH<*J$AaVS=rrozVWf7vTJx`gt_CCQqQ7>)GhbXbRs%GxRRj!H=;yp2e!zs9+ke zX9C45@EL4{?_dSIh;Hf%m!&1jV7KV_%Q*jTq6IY6#XaZ%mljG(l*4A|<{FA!a0B{b zlBIB3>SMSb_NP7?ef~pii2qup#8sz-hV7f!5Nk;5>i(Y-R*VJ8Mi_+ z(h)t^J<-iG3|ru2G;?pDYx)j)dbVOc{5D?CUo`BMqUe%U#mh0-n1b)@9_Y;OLp#`r ze$m*84xF`E7^n~$`4#AWJ1VwMsigG10jMnxy0n>2}L=vl0VE3q2x$84VeORfkv z7Wq0G?F&1k<<(BnN5UD^d`U{9l`;gw9z zzmXq~7yd?5maTL!AG&FZp&eC0Gtx9(?~Km47uxa2=wx)Q??T_4Z^rh&(TQa%la~5@ zkiSeaG_<9`<8~F=(KYD6H$*3*0nCZ@M`QhY^cDL?tbZ1-e}@Kk91S?VZ0I*1+HWZ| z<292M?4Tj~z?JAg!=e+=8|R>zxj(iqLT9o9UE6ih57AVAg|77>bPt?C1IZ{C+Vi91 zCCgLrh9>9@9peqx#2ZGT9Ztu(crO~zJLq#;&>4M!F6EEtz-OX4%7>YkMEkEEZ5rD7 z{!hV<`bCGw8*YvD8L|FA^vUQ-big;!nQTM*IT)}1hGyn08c?nZVN+(Jo4PVy>iNH( zf-@S2b}$KzbXKg-kM$+7z6#wdZ=nNhM$h#w^zok9D}SureG5p=w=);<4K zDL7yQbn~@Ck54aj%?HKnBjWXmXos_7`}|mc8r@5)up+*P26`0j_jmNUi)diEDslcD zxHJXVxF&i-Q}n~76V|}H(O2#p=#2NE&;5da<2j86d^XlIDu;UZXnr(+VrYgcqk+|| z%=x#21~j;vo1!!B5WN}=Y(R7bI?%1?b5qg#XT(9mOuc85Li0xak0`)H| zbN>A%axPw|S0x<3R_NyHfyMC#bif&CKy%T6AC5kS9jLFsdUyi;rBt?R_@XidU7DxS zwSNr_=H#rE@+^G z&>4@yVK^Dh=zcWihp`?0h6Y}{F6ZCP(TqYZ?2TTSi>7iu+Tmkp3ZIYHSEHw5L#*#X zH|O{0DM{1|17}71%Zu)fV(60g#_o81J>PTW3$#cD zkW3Vz;M!)QkyS)bLo@U=Tp8Pkpfj0-CGbAN@CyP0&=fM&DppqX7+x^;^(DXQF#zQLHaVpWA>ww-tTvYxKUO@%rCb zje53L1n&8-OTn47Kxfd~3pfPrXiU656;0v8=zTAuoAWhvGj53O@1qmhflh1>nu+gX z`;X}T$1wH%|5R)^k9L@~by}i2=0G28gbvsu*1MoHx*DtCU^L)+&||g`?dM}OBcGwq ze-rD!pcy#Rn)C0bDcdFt)Du0={jdWLM`!Rdx*1Sj5I8WQ~yS^wkqTUR> zJ_+3;^U#dEfCl~wIcA(bMok^i3o~$;8%p!vS=rzoRp|v}4#jMbS^cO6XcPL$41&Ps3<5u$i&_5j61U z(22Z+X5cmS*ltDx{1Q{Y|MyXFt-eRs_zc=%mQG=Yg`;J$0`0ZXne;_F8i@`xF4iYw zdFs>9K$fAWVkNrN-(os`k5_p9f22?g(>jMYTOBlz?r4gyMQ1QN)+fd5v(TlOAKRZo z?^}sx=rwdAAI9rn#r7Z3{!d}*?|*4s!h^Zd0gIq(S00^Njd;Bg+HqU-zFuemL(u!j zqW4ciXPk`f>(ESXMBfMRqR;Q_!udCnZ{vky(SOl_vUd#+6hR-Th|Z`Ux+glI&ksP) z`$%++r=kPRk1j=@TaEVnZfyU&E9c(}2V=t@=s<~XX{pci+-Qe2&|g4p(d)y|=fW{|u)98%PqchCeJ@i)`oj@ftQ_W+&D|&x&2n8P)kM8OudgJ_f!_(;6zJdnu z20Ein=!{QAFU0GYULDptAKG6bbV3!-%vFmv4%d^34xy0fi_Umttltsq_o16=33kO5 zSQAg7ui~;jLdR9n`x>D$Yl8;R1I^ShG!r+)`fM!Z=l??#JburkGx`eMTtA=zoklZo zNzd>hlrP#I4d4#+{V@y6;p6B}tW8)M4`L=>+ADnbtAc*1?SsWV|94Qh0~ev2CV%hH zK_kqhJ^<_CRBVWAup%Bo2h4X(8vl@zKUAU}4@Q5!&x*c>S5Z&&3E#52qZ51plf5WB zOQANN#@<-5Z(8DdoQAFOdvsTq=@(LXEjr+>=u$k14e--wdjD|#8>5*Wi|ud;xLG&^Y2<+puvc84+sO4MmJ@HXb1GcKIkSIf-cFpSidb^pN;+wcp%nSqHDiCwr@tC z{|4RUM+R{IUGpIJY0UXGiv4LWdQP*|c%u?*F!*cq?FS@<}*CyHO2 zmY9UK@v<~FFZvnq_TaF0-c3?)W?N&!ZZsqN(cO9!jr0O~U#{yypoP)hT^bFv4tgvb zqMLFYn!!ov^K;Q9d;|?(2l`$~?xs+eLZKmPiMO#cI#9NuVW52I0L9TY%S6v}op`+w zn$ostKv$#p4Mq3FgjjzGeMP^GW_l;`K1n9_Q!t_*(6#yrjrb4rfs5#X1%`#|MbP`p zp-WK-?XY&NH$wwzk4~g#bWprL0v%^O=JNbcjW^6g2VQ{w6nh4JZSqPm#O}qh{VfSS=JSdDsn^myKi zo$xMn&+J44%oq_iZEkdFN}!p`L<6lDt%)v8Lo|SPBRK!A?UgiKiM_BqK8vRC3BcdPtGwR^+nJ(W94W!G?U}eC7y}JJpU^wIMDm(X4!?NavvJd z_p$xA*!~wf({t$F$vQUNmos`fI)Soi;I+};34_ppC&cSZv7+by6$&1oJ?Ja(B(}mj zSF78CS*Yd(f1Bg9iRH8u)Q^6aE{o=eQ{>Ng?!ldCcwkuWbQa#tT=X zsqBw-JR-K=g8ixA8S8t|0e(dH&R=Lh*~f=W7DoFiiKf0Pn(5}zZkRNc11XsL;b@2B z&=<{g^!U7qW@a1uEjIVfVQo9(Sn6Z34(>z0sN}jOyy;qFZR)q68G8=>klK!Zk;%A~ z^Y3|VdTaQD!&vN0eKGoiIgDN}G9k>YGkVZ_pgZc&FFxiqQ`DO8sIT>lm3kc z{vWy&X_LbHp83{SM+aJhrSVlP zg}dVQKd=?`WcqC(C2i4E_dz$&-RKvPN6`RZLI-*`Uf+pjsDFnJoR}QUkKR`XO?7QF z@OEgwozV&QMea)`hQ$lFM3d16FqLZbffZ;+>(JD`iEgS7(Z3sqkG}Y=m9hXN6?N=#rDJy!TR z9q^G@UyN?P=h2L=NBi4?p03?V3eM;ktb~^kkg2YP{Wuc+H`Yg>nM_Wk(2v5MSP8#E zZ@h?pN6T}6Sc(ql16Rd*U+h5rIy7T1pc7ah>mSAXZuB@GMxXx|UE1^qQYI5QD0ra& zy7tA(XD7EcB1zkL^s<}^!~r_Doo4|*Sn(`>yOC+ z6h>39gO6|+?m^#NjUEm^nZ}~;^vBTw)}qH~Bf5*C0v1{jo@<6q zv;!Jo*9Dw^JM2S)9Sw;WCZO%pu>sDDZjRT_pnD+gkq~%Z%%ollU9whas;@-*>48pg zAR71>G=sN1!udBvQ)sZmIoJ{xU?bd(?$(?OLx2VG9qJX(J@6CyeDz1e^~UHx-O&C9 zVJjSs{!{Z+_%I&9DR@V6QF!na8d0{#LdtTZ9o0kMknPZ!4MPVUkKR8Oy>AwJiWZ;) zKN;IsqHDbY&BQizlYfboF?p1N9Tj{$Y`$XA%IFt{Mra0lp~q_=x_0Bx0jFR!oQb}O zR%2V-ftgs~iSVbJx@h42&==DU$b^!KDHQBzF1j`kqBB~EM!XyiV0HA3c>SGN-x2)^ zo%sQDll_K1{|7qoIkf*v7KiJ(v7n#-1t^%>3TOv)u?e=usyGeX;fv@@k7Fw=_GB1f zAexck(VNkLr$%R^0o;c!#bf9T=tbKZKk-hyuq|Hr#tXFnf&N|aKQ!`UPld=c(cN7h zeP2{YJ8l&1jAmc}+Rt_91jnEgoD$pb!ekX17EsVlXv&VDOYkS!ar%<*U_mTTy)-)G zj_7keqj#c#z8J5sj&4BTA0MK7?K?EE!%H~-z9@c=H|Bae{4<`*(Vy4XV=9&CgR{{Y z-;2)ViRcRS{qZ_FfvxEMyV1S14_&%LXux@whJS+6WGUz0kKbW5c;htm!MWHK??-q2 z*XZs(hHWs%Ga)lqMhBw*=;?Sp zZCSXU9lc&SS_XZ-20F9)*dE)UGn|L+_WAMp3+TY>Lp_;zi-M{92<><$+Tl0RLuiJM zM*l+ZyBO;^p9`C{Fq*Ln==DZuCflL?UxNmC9r^|wi&;JYGbtF+-SLL|(FdN0?a!ez zc@Yh44LZP^=qdRC-SuCf0qjHX{}uhIbqZTy_2uE`_)Ta=c4O+_|35;($WGXR=g_rI zdp^{2px5)E0bGF&P!7GX8v0y)bP1cr>z!l02f7!o!{&Gsnwgg|_4|Jl1$XVISO?Fc z-&|_E5N6sQO<520!)OE=z#KH7`_Ta&!GX9ewqHQ+%eo?DG&ef%<>+&jR&f6PFsMm` zOVAcQj-9X;4vX!NpiA;N`rtCOzZ?|g&a|4WjB z1D-}VPx{Ib`DN%0mC)-A(CcmE^=snwQCOb#$IrRftI2fS%D7tGJb?_VQrkgDx88fco+3!=pMV}B$ogvds@4#zQNI~S;}Y}_8`;;S zB^F{AtbqryDdt)0E1mP-g@WH+N252~gZ}b)3q2LTqnj~vT?`DZ-+?V~6+Vc^a4z1y zJ}of>FQWGic`fw&Fgo*hG4)=+Dt`V~e?5E`>xcdYV-~i;<>*(bpJKi88{ztBEKB?S zSO(XinfLDsUNK9-BZ@^JFf$H{6oPT$3>vzJ8 zZ^W_GUqsiq(B^RLTA%~>z?rxWD`U5J!;jG0um|-`Xa@4W7yk8{5qLHAobQJh+932( za02?Cng4z=ywP5w!4z)AxoNx@&>tQ%J`5>*6K|q^0R8;G@}sav=A(gZMKkaxI^%j< z!cVS=IEwlQ=;^5baX2lF(f31(WGr;TtTgmQ-%$N9KVFZ1$V`gubJ6?eqi?)Lv3)Um zOrMXg!#vbCqxbJX`}q=m9~?%%VI}{ikVzr`*6`P>I?<6>mG-Bj+oEUC^IqnY@J6eM z&a4Ldf@y*V(ka%jL1%hB`UV|`zM5wsfg}?PDfqxjbjEMQ8@8e^m~W$p(Sd(MXYxlh zZClto`7t}~J);BBCA|R+a1ol>WwHKBs?Pb_K*3agh<>_ljrE_g7xiQ4v1##X_=i-h z(KY`TZ^dk%g^q7W+vlL~`qgNlucH(D2w@@!&(+2Y?d#Bh-o#YD=%(8q>tCZ8Ivm@7OHy#H z|Bg50{xZD5ilQmH8NEIi-2+deo9S8fxUEM!d=K4hpTz6?&|~&%ynYc4_>w)roY7=n z3Z|?e8hHtH2`ZqGS4C6a01coc`djWwtbrrYrF#_Jysw}iIv-;up1>ZM_p7i;hobG% zkv)}6ET!P)*^GWXZb1Y17oFLKSkJaM%rGY!XmK<{<M28Xa`NvHN6_`co3SIv9W$9dfx+B374Up?o%|t zL+Ci?(0&WHcVf~_@ec(DI**?7{NIEdYol+(c4)`L&~H4WV|^vMWNXnG9!B@lf9R5C z`!<*xGpS#OX0SfqhONKl{BNW177crG_IKgUH1I(9>h(0b$#$Td??1GIjDw+`7d-`q z(1FXMOIQt^X=C&hbc^*N==~F-GY=+1Y96A&wR}2Wcs05S{VUj~SRa2z-;gD~4;?i^ z1M3j$-J%1~42(oGJRY6M1gwn9&^PO6NeX2t{E9x1_lI!o3ZwPX=!`3&GpdI!MMv}$ zbVrYAe{`T5;`Nc}n%|5*pF}fsADV%M=<~^CvG6kbz#HgVeuTbw4xj`7g{Jb7L*WHf z9L-quSZ@)%8r{6Z(1C762cCfrcn> z&^O@;G{D4YV=}6r9-@^b{n~4i=&vJd4iYRrJBP zV*9&j#&)3he;3=2q4)oTP9*Jcc-eKg9CR$Z|se(`A{@7 zW6;lp+hY6F*giMb=cCUrMxTEnUSA*E-$9?>h6c714Q%hv$uPi?*l-H%D9bNlDe|EK zT#ja-44SD1=zU$IebIrhkM)~leHuF8J!n4qW3M0?JuMKzkvqw5qjUQWNi30HXMnbLU;28bS8OE zgh0!p9aP2CaY6&`igtK)ygm@k%#G1;XnzyXrJaLLAo&0VBYg}#Hp}A;Z^ZV^=#8JD z9ejlj^a~oupXl=!&<~xQC&LS|T<8P}p%bl;s&oDu#fHx4g9Fi&4v+QG z=z|l{2j|B2$IzKPi>CVJ*uD;ZkGvh-i7wS4^!_vGgwp;XU_bw}Q!rKe&>5FRZ>)~a zxDgsqYjmIvXouaOW6?(xL3U19}Q@5tdIPY^Y0DgXmBPI(3#9YBYhCP zz9hCkhX%Md);FMmd>q|@z7hAJ{p~{o`8n4AL<7Buj+5mS=id$roC*&VMK4r92damu z8DVNhXa~K}ne;;g9EScuVI~zkyX=33>m|{S%c4tD9lgIrv|ViP66?u<6dYhEdhAA_Um$KrKh@@= z0ltV1_&R#u2j~D_pwAzU{)TSaztBwlhh{Fz=@3Xhw77~clcG@2YdMWpQO--3%jrp zR{AIWZFeYkr2Yi9!XMB-vz7Tb*b>W9AA}Wg2Kra;7x6|sjIFTS+3;`GJb-nm{~j%S zj=kmi??b`JhhiIi5)I@4_Q1=}hd(}zLI+%g{&@T!I=5er*35U8r#5R|e4lz5 zoQprl)C9}d6{ zS<@3w;6eE4uDdjup86*p zFVS!b7tWx8TtJtkUyk(DUpy9I2kN`9Jr>Cs0v(KXsjooS{u^wFm*)z0N1wYJyW$S4 zfW>mBr%plZB!wI_Tovt)p4S`DH`!=(DQ-fK&E)7D^qfD8KL2E_zYyzd(9^ULJ;tA) z-}`^SbUYtTUZmha>3PxclGod9qP!IIQbsd_?ThV^* zK<}H0_V);S|1x9`B@=5Xn7X&ong4@!Z~@(H+482R-Ur3d<5v|u1)b0V$3~Or=3Ri^ zw-PJi7W7B%NwmM*`O;H=j4F-AJpXMdRN=z4=x(2vy1?Ip&<;0XDf~Rve~ z=nswqXo~+v`?<7W2&5=FU|sZj4=j$?V^vII(t%b{@WIV!eJ9%SkLauTJla9-%R(@j_VkYfV(dVB+pMM=)(k`r~Z|zX4s7SWb_!l7VE#E z9aJtH0;r7!)DArbebCGdK{weXbSdXxMO=(7<@@NS+=rg3?+bJO?cgX4zZSJQ~n>bigb{LI4HPnO8zLXKU<+y|FSri{Ae!evbQ-6n0WrRWz*G z)MCL$(THEg9k>Mz@X_Mwss9Ro))HX`x1pydiB4oLn!$MlvUQ0R*A3Lb`j2t9$V zF!>n;*C=<%^u$IiiOy_4+TkJei^p&1`TqlN!Sgr}Zz>gDrQ7f(>V-1XQ~&sV4tg3+ zqWxc5I%K3+v^ugx$wVs(rnV1y<7hO6Q_%nxqN#iuUGufE{sEfGuh2~WgbsKfeZdqi z6WYt6^`_|kozcC~4@-FdZ;ChEhYs*W>HYbe+*X+ebMyC zRyYd{=mT`1{pkI_qwk5V<-#c`gZVxGZ7CSpHE5(apfkP|ozYz6Rhd|dF3l=5L+h~y zeumTV96Et1<-@@DqWwLCW^y&UncqSu_Bp2h{`W(?a1nh$TwWpESQmYPbVc7-qhk9! z^uA|e{cZGEeTk*<8#Lhm&`exfF??!fqI+fpx|iOs$oV%l+i5TZhtL`Sfu7HtmBNfK zLpv&g&Y&*ZL04>neXuS*if-0V&{MGk$KaRfr)R6m;rf-)o|QTOcHEZ+Bfbeu@g3L^ zXQ2UZLjyX9&h!YnwkOd*vQ`P^!}ipRV>=v*_WJ_5H0#juK1BQdCP~2#f5%Mx8@;he z)vyPuplj6}JtbG6*M~(XqJiFpex=%hc3hxZup(BY-VEKmqtNkgNBc?6piq^<0&I!z zpc%Lj%~n0UNb;Z$^hHxSDta5bbhFV-wg7$pDKrzyu`?dSrdY2=dg^~gb2GAelZlTg z4C2CH*Z_Of3~MYEc8dAFyJ-4IK8IDIc(W~f;PNN-M zKm*IxEO-UFyX&EWwZrSN4|*E@hu*&v?dNB7oRiHs|E}R_8k~7r^I#qtT3oLJou%s1}6nvmH`oJ}4#KX~u??5|v z7#;Yz=*wsTucHBM!fS9_yq?)IT(696Xs?Yf%|yHl&!Q7g&TJJrcmUlCi_rnspquV} z9EabcyShv3aR2pa2REaEO+zOz7tO?@=;mFHz879c_tfUtz8@JUnfR50o8&+2k9FFF z0PaTzcp5w4OK8V`pr2}qwjsbgXvdYJZP5>>!DzsDpc8onGx3#peMjm#=kFK=Kjkvo zg^o+31Jy(`(gK}9Z*-Rrj?O>_ScWduM)bbj(QnZ;|2fwGMf=NWA1sQg-~Tl!*ijQS zg}u=<@$DKyZA=yM&>`v;@-e@s&CgsuH-WXl$tD-l? z>oel@g|U4lx>WC@FQnaQKZkK3{)6_{yD#V8nT_fjQoa^_;8U!H-(g+M(=T+;1`VJO zx`yM?f$l{+T#PQ=t7ry3L0{e9qxb)Z_Ftrb=&wqW!YwqkKvT2^P3ha{8f}T~`3HnO zas|2sjnPz(MW36D&Tw|DKZfr5=g>Fa8ub3pumbKyUu?+>6zm}Dz;Imhqc4Vq%?pGP2(}U^`B^J z&!Pk69TV!s(TvqX*S0~dPe+g2oLFCgPULZP%~!?izhE!w$DBVv6P zwx#|UdMv*|1HNor$k-J)hI(zZpC#yh&!aOufPQ$Lj@Jv_6gKGAz_%yl+vri5WmOy7%4eg*k+Cd+5z+tgI4&Ai3qiZ`WUVjV? zU>W+{I&@FHgWmrsy5z|(Dfr=W0PVQwl#udr=)jfHj+&qybU`EUi)L&%I)TaP{r95x zKZypk3Vk)di$4Dq`rMDmbIHVM3U-+N_Hdyv`amVL;}&R#J>vDDXbLBw9nM0R;vsav zb(o1C$LmMX0Z(H(UUEmsNDl1o=YL@e)wwVNuf&De5f7mmsWLS^u@&p%9e4p`ki4SxzPa1qN%Ts2HL`Q&wsDjFdR!$zXN@BK85b?_3`?4G{uL| z08XJ9x@3B=7`n@=qo<`ly0p#EwVoWE8L!`mNsqw-3P$)i+QCbhdLu@+pabnk12`V* z7ozz&Mc!Wy9jFmrk8R@VdI1gSCv>TQL+?AAzKKrYv)FzBo%vC0jOAvA`v+q&>Z7nK&Y4Lhl_;#Cp$_iDDwu0ls5eCG zqcIa7#ijTLx@Y>#4$sfQiqv00-yeIh6&9Eien<4eYSiaoeO!y)|4Wj>01A2MhF>P5 zaV+&M*a_?26*4gmucN*YozY)tifh~*{;ItIyHn3PFZ{Z_27T4Oj5YBSY={3wTig>e znVe0bITt>{Hkfg5_+rojP4Qwh#m`6ohoz|R#7sPi9@l*L1uLLy+YCLP9nhuh6&-^v z)of(5ClilS@PRkc&G8<3oOZ_gar8}iF4ix*KLl0=-Bi`l&xm^HtGqGp#wqA=>-s?W zR^1ybQ=f_+^XKs?&;Kb3MmYb$^wj_R+!t|h8b3513Ii;jA6_`mqLHtO_4mx7=J>##H4Xr1x-&*_BYvjIJ3+p!CNkIt;>g7nn?%(fO@MST%| zi9eyI<$sR^x1jg$Mvv)U^mrdZkLL;Ww52Z$r!7Awol$8D4Y4j-AA<&RC;GsAG}TMd zrFtQ@uR)h=Go}JZQ~o_VE;11$3>=ieLa(%>fQf(|$&-Y_101I|G2 zTa4bf7VYR`bcWxc1OI?dJ+skvGt#I)Q%0JCAP0WRb<;oMxhH zUmcCCC7SZ9FqJZNz?*O)PC}2F2Tp>66{9D`x={g{*O}d z!E#H&d!TN#E1K%-V|@ymk@>N{BDxvf6MNB9a1ISL&(k5Zg`;KB>$T7cw#C%ve_sj? zGzwj#>F5jT{@A`ewr@ZK{TNO8SLipRL-G2b*ob;!X?R~WM&G1Mu`X`Hr|=K7p9i1e z{2S2{3Vv#>!E$&U4WPiY;muYBUDF=u$LCaZhKta>@iZFvirD@-x+I&>{y#=jzaRZf z_#U0W@n<>z&fs4fjQrANA>uaZ@f(6}y2r2%K8?N)c41%q6|cdz&xHY=K#%ECY>&&a zJ|05@&Ria5UJ0!?T%HV(w4}k0%dY5V8Holq0iD5Ybki(~?JLj}z8TxM#QI+J{-1FS z{u|qeKOfe7BD%@vq5VIbq+ld#(SbLhd*MU$O}8(4GMe>;unCK!0oB1)*cRPXbFc$G zj%MZ{*2DTM(o_F6jnQak&R|zeHhVFA*vv!USYM!fp~%YcZT1@MLHz~vE7d>f$86n~ z!VGW4tEeBqfmnT22<#s8{4b96r=zb%-wh@cyC@j>A#^jHiS>(UstUdwI;w^qzgp-X z=!kC8p=io)M|b=EI00WmH(AM7!ltc)*4v{?*8}r<{)fbdo6*gdM3>|)^i}yF`ru2k z{sy{syU{o1q3B<+J=^NA`wOEP$V4Yt4ohL9Xg@6J=l>K6uK8nF0bh=Ofj)2+$6>Ko z!;5Af_Vhk9Ll=>+Qi&pKLcIyvUoUjvkyr_*p&5G~-Rzq%_3!`fr{HEgfo34f+OUQ> z(FaPP*XyC@yi2rSbOf5AiRdPtfiC5J@%lonL46sT(Op;vkFVwYJA=%1;rup6Q`-`K zQFM>>LFk%|MN>XCwm*R0w4-4jN$R*gg>L_h$4BIupm@D)ifSk>u;) zoDRhKG_1v1*yfG!`8@#*;A3>49cX|*qBH$7n(fUn!(v#5_Qugc=+Y$dD!dQJ;C}Qt zCp)|q2AGL(F5qQI*MlEIGXbFXsYx4FFaQY?WZRCzGxEb zz0r(ZhpGSnUt?m!t>}Q$(NsT(4)9#8ufw|3KSn>C&S7n=voU;Cy8&I}x#*kpReS;e zLIZsA?U1oo&`;41)$@Olg1hxBdOY%N3LRF%*3_>=uRny{@pvzH{dLcT2 zpQC5ciDcUxHgj=w=Jn8VT4B;-F^EDnoQj_B7tytU1C9KH=$?4}7j(%^qkG_dwBWm; zUM1QZ?WZ3a_;B=v_Bz(XJ@0b<-CQ}|3l|EYGpK{EWgB#cy|5p?fNq|P=;wUF_ruy2 zLD#wt`r>Jhw_pb}fVa?zeS~K23+#)fK1hZ&p8P@hO?DT$yFbS&_!XM6v=2kaxzWH1 zqbaP2?&`MaE4WX*J_XBDzXx5qSJ8k!LId4~p0Yj3*l-Y?`7t!*XVHOkeH1=ci(pmi z6VXgO6WjkqXMD|;a4H6)^QSw&a?#jTpjejW@xHAp{Jxjdf!-d!n3x=-~Uh2 z;F_(A7q+3P`Z2a=-x027q7OET_CNy}jqZWTXu$WN6Pu5{aA~}L3g4rib!Yfn@W(qj z|E6dg4X*tc=$f8HKV&ZdJj|##I#78`$2#Z$_0g2KLucMCw)a8L`EWE7H%F(T~bVr}Xhu>u}P2fpl!5J)Dv zr>bHmwnC3j76VAeXp8v-wxOrA$2i%C=G5zcCzli9GOQ`4B7gGN+8t7Iu<-5_% zn`M94J0;Nm+MzS#Jzi>5r+fv{xdus-!M*bSe>R(Jxh!I}p{#^y#BqXE2nkn`_AAJAYR+t8VQjz)d} zo#9`xJ^%M1fKq6C12o`X=&l}y_Hz#!_(SM>WFdOrVTI&McNbPns`rH8^2w?G3;-b%p-r$pzW zGkF}{<(tufK0$w2e2b>;BKF0CKZZ<F+otrI%)*;p1gqMPU-I=$&Y&c=G)U&84rgZ5h!4Y)aaUx(N} z5KDXhM^P}P_n{pw#ZtHqE8=c6u(N1KX-7hUIncE)feut1z1|c}b-Q@IH@f+T$LkZ( z_sX4^>`39Uc*8+-%}$_enC)oT#re?=3Zr|XJi5thqJcGywm}E(9PN(=I4aifKr?VJ zn#rX{Ise}91`W>qV{|6_(HGDWbnQ-~1Lrvw1}uUOR0>mp#P+7>eQjdBTfE*2UDE4f z`z`21ryNU$l+CB1D-BQJwfGa>j%|MpH*P}rz#cRc-=V4gEqWRa_yW3=Ie!ZiDTUr& z9X)Q%Fcly=;cm&;a2=*nh0g2_G@yIY8^1zla1>kPIc$&3kB9fhY&3wSn29fAW!#Rw zi2g>G>O7kIj1%dJ`!QLJf)TC2)GkK@*?=CWZRo&1p)aVv(U})K8NN4^z&h01paD!m zH`lCKe+r$@O7yu6Xdv$+<0TWHQ1F3W(O=^YS$_{5=0YDRitdd}bWhYk1MP?o&@Wye ziEgsn&0KZV}E9Ix~IzZP$}?2oXSu0S(V8C|=&=n}L+JH7@Dcr==kIp~Y! znRxw;c>U9O{ik?6?a%Oi;c~3T{hcxO@BiOM!PLz}J9-cuczJX^I`I4GuH75!C(+H8 zsPWn6iRjtE6;aBurtV{bEbn_m@>R91l&i@1oz5fk=4%>=; z)yjD`J@xs$5`Ewpn(Auj!i+j$1L`x;Q}7nn#|u~$>zxlDVnfi+j=Ry*v>Bhpod2aK z9>Z7uQ8aa>E`|a6VGZg}qq}`Odb~JDsmxWuZq%kkKf=<~3#4VF zzFpUg4nQ+80|(%1Necc6CU1I1DwQ>`C-s3i4Oe4-teTOL`orN|yo>su=#t%@B_s9c z`H#`1DVjBOJQC+p{{Wp}=WH3N|ANBn=zTpd$w>I^Ir$8Q?i3DVJFJ&IBQXPS#}RlI z7vj)MGg99R&Y&~BB1cB*-+mj7ruYQU^I=KkqNgn~^$hjWCmXPppX(@a;7IJcrI?NdAz)!wcAy`U&iXRSIS#uJ`;;r*I<;hwx79a#=>|_-sXw&2G$tUq^q5?PoAI?dQ=K zQjS6)z_Mt2JM{UU=qtD%dc3d2oHzmV`uU%vUORxi*V7ALMQvV32H9GKg^bI;29r#}K{4c`PfB*kE3I%9bi$?fibU$8B z{dY9L>_x&FmqtHC>Z0vk&{ysC=zwFQH=`4o6rGEms6UDZaG*#=`1gN*rJ*Yg|6nGz zE1Hq|qtS4*K0DTzV+Qs2u?lX9^*?YF_0u>TuPYYz#E)oZDi+U3+-*DhnehwO!+(nN z-#;}q)k}m6_0U&sdvsF`K(9ZISK#yLas2=*;-Og2aYaVzL#8sCi8@#fn_xS<5i8-# zSQ__W1^hcn!IYFN8P@Vfbbwc}H}1eOShiF~>P`0$`r)$&z3(KtC-P)wq+Uo((1CA2 z*ZN^}#!JwYua0hv*OOZ*xLd!B7Y?C&;Us3q^wMDy=ET<2t73T^hYs)vI>1u&{I5ax z(EDhBU!(o~9k1solfi!{hA*wi{mDejvLUh_XorK*%{3bD!rJs6?VZrIeH7g*+t4N4fqiffx=E{*H!#j$Z3=$dZHETX9UWjG7Q~UUeHyyk=f&&K zqA!}&vAuSMjMN{kx}XD$$FevPUGvA$J@miW{LenH4iqU%A?&0}nxGFflp> zU6N_&=lk5){t!BWN6^$ijn4QrG=PoR9uJ~{l&=()v?C^cVcbZ;PqSrs2Y!aGamUIb zmE$mV9o^lZVL$v54X}EZ&|Vu2urazx+n{@55c+HRCUm0rq5Z6>!uj{Oy+wnYXgfNC z16T|HLIW&cl}&>~FcW8^ujJ+EbL-K5K1BDyIQ6R~!wkF9 zP>hBd=&pSdGx2}%#&6IMk3XW-tB2z{3LRiAI`9YR8*@K4$3L+d)~XQ#9*1UT8k&Jc z$yj(AucqMzbY_2}5ofCzIw*#wx-WWtAR6%KSf7h_ybzuFYP<>GMhB`|E9`-~=ySKD zOO;$m!4I7!(dW>$Ux~iEcj7WUh$nGY?TkeIH2#WJC%m#>tQ+2xf1#VKQN7ULbTrU8 zXaJ9({XK~eyb3vG$;4|EoauY$8t#epW9V_afUfc7^+T#Fp&d6t+dIel0Q5WJDC~rD z(c`uQz3(@4;Q!DUQnm*1{1>HQ2CAVcu7kcn8em-IUO(Cqy}y5S z2zsn;L<7GQ4fw&>{wzAqn`mZsG>+&0t9ap8bQh;L38^iC-dGuZuy(W)x;L&vH`Nq0 zuz6^pi_yTILj!mTo$)60{$1$(KQ!U|`@kt09Oyjy8IYxE2p}K+zpQhBj`RDz{-h~W zJ5`!GwQZ%g&D6H1PHo$^Qros|+x&m_%$e`X`+Lt?xBK36_Sxs$`#dvEW|EAaxsrh5 zMxr?jxD)+A70L_h=xUn3C8&qv zU@#510+jH1Pz}8Xb+3E@b?^KGwbS?o-8)PVYF!YNZWZ%41(omY#zZ^m5319lpc3YS zy4yE`>A~Beu5H9Z?j!XBb?;;awUa>5)i@~OmY{Sxf#MAXbtz^Vu5rhmj(tq@FuDY4 z2lqie9KVCQB!59&`|yR`N1p`LIsnwADh#S%IZy?wgL*Wy0`=$!2Gzh)i|@4cSC$_}7B|;2BW&##>uQE#Y+UG*b!pU0ecG;p(6oYY1wG?G5{Yx-`L{8d?DA zB-U7bGpG~T530fApf+|J)YpXXKwXN@pc?+^WTKlSTuJwlBn8!RE>JgDO;Cjz*}5$# z!ET@ggDpM=)Fqf<>t&!evdM5as0|$j^)S5xO2_%s2tPn|k4Af4SfqIJF1&VhAR6}n-HS!hIC2*8;V{}k{ zKTszTP>#pHI?rr@0-%mI5Y&-Z1yv{rR6~70J^Utt%3BTU=Gt!SJ)oXiPl7t3C!luz z8&o4c<=rQm0F*yvc^?0|`Lf}VaCuPcAW#WTP&eH;P&=Cj>ZIm_+VMtELPtRD=o099 zsCE zLFrus)$nV>FXsOX>cnDKbU%!pzD!gw095CBK{ZksbX^KtR|QqD4yco90%}K{K;2Bi zpswv=P>rm$^?pzrISESlHmIBWksF0;*s(P)|7dKoux!@#>(S z2U>#?bQ+EVb#F`swV_4kUt#ObpiW?i%g^I)uLVwk>hK(>r{Jr${t8O)52&5^RCYI- z7*qq9LEW4sK{ea}RDmE+C)ftm277?w1)G09==%Bp)lBs8+5@W4ZBW!Q0Ch=+fyKcY;9BrDSOy$bjmLjBCYP&uyMEUr zLUs3dC`G|M_&b5w!IfYu@D5l546NaPF?kMHp7m=`{=7Baubz(w{aHUW^seQ8xh*AF z0)IKMFgT$WkN@sW&RZa`w);is(O`c3|G*qz{yOfPtP@z0bzj2+U{Tf)>Uz6=`n?R8 zkM&Y80K5Zl_XXVPA2X+-E`N`I11j<0-IyJbj&N^+kr+CYW*cIIr`4tCc24&Zy`13 zF?N<4!R-_%1y8mZ?1fllFuq&F(S#;a<4b0nY3-&u z&RP5sMX$i024|0S>{Qb7_vanu5%<^=5_UnpV>{1%6C12i=WKV(@rvMU#ybSs66{aXLJID*-7aSbak=h4 ziN}ZMOW`Vv(&R^m*BM_V<4SU{>F&iJ!S0u~b~(K8$#}O;_dnMZ(VKJ@1zbd-yfjb^ zu^}{3!^jB?z3_L&pB6APX4_&nB!oRj!N z$VuR2vX&t$3+X#u>2ES@HeVaa-A#BdLZhu8~dW4qy8 zgX2VFjp>BJpPm}a;5w@_>4|7KoP#OQ2Qq&z-eun@vVo*n#D>Gm&j^d}Es0IZ8HE2g z*cSE_cn^pbz_$<{U+_4>qqmCqPkc+10f5YHwOEI znJDBSBw4z9qQXM9z{ljUn4-zH{JxMtI9TPk{h(qa!|pvDf@rXmqCKD4$PralNgh zd1Ycp^!xX1DZHAb%?QY<8Wyv}+r-vWAUnJW#KMD*;e|mg51eZV^Pq?{|6Fe+c3gA;}^^;H`Vd81N#;E1QSdIB++E0RFJ#r?4D9aUpzl{GJjkdLzeE*0y zKsOz+$Hv_OI`eTH#b0H5;7n-lm@0!#AEAO z7iL{GlrJH9OYv`q&u4Ks_=#mlXY?+k-I7=w{K?^`H<8H1yu)+-7vW6Fjzima67u5s zMl2qQ1uZ_)FscyY7x0=fT2oL~4POU#C|gHvH;UY1-VcrU@Ln?N!CB2Z8S@-^G^b^^ zA93WO?bkVL4REuk!b&)je_vaPWK%8 z`p9fYI7T6oWI^CO@TqIXkwH#K(VqB+;u}fPcdTzv?2~aSfMKo4vdm9ffdlX=TZ83j z_!<7Nb~^RQPtQlDIC>zQ-*!5R>9c)oy#&=@P z)GQ6frNCi)`_Wy`h{HNDBP-1dq8JabN#vba@2A z8SpfLh#a4+JHCT%qHBGJ24#&I7bubl@ez#LY7{O%dg&-`#l@S&x~S!3qIrK}e8#8C z9Vp0kulzi-y*p&>rJ36pNVRI#|p+k_@?bpepz@n;zhha{(_-4 z(#{=rTy)a4>@ni=*~tXfX_?1i3`VdrK3Q)>ejzf3k(lCLS+9p9OJKsM*zJCNOTfSQ z)4=Nn|GYJL9j&%(V4U$UQ_H^o;bwvC$EXPDFoeO@STH!ob{!evnQ&zJD3k|(HN=0w zser!-dDj?eh^K=qtAT$w@qCPqaI>R75>7VO4Z=~M&*gGibR4q82rpv(&vus*p;Z*R zNb)iK>q-7!+iJxEJvs1Zk@J@I1<23HJz;f}n~=5J*ne3T3v9{9nmJaJoDjioCZ-ok zn^QE0t@-#%$0K%?ff3(s)T?lO$&=|txjDo>l5@*^@(*U`H)*Oc+|tAwvv!^TVisFi zoE6KAH;^0`81pGwm}c(&*D+rqhnhE{$V>!#QD}*Z+DS|SJBQlwbo71ElSN@HCFdn; zX9E@!5xEzt(?6`!LWl;gp;!y%OCk3JWyh>Y845sgtY=-3;_FyfgL9nqDDq}8Hn=i) zD1$F8{}jBvjD-5g*f}U9w}7~JXheT8suUexAl8-j6^h@r?(@M7rdV-eA1UyM6N&>~ z!sp8@|%iaGifo7E_rf2kRo%$Q8hW*h7TY(nt=9 z#}p3{8?ki%Q>Y8gwPBp5iA2OAnDBnq$r!TFjC=Ug z!kdn7vK@P4bWf4P$EP~VTT$_&;r~Fbiu!u*t`(1Ege?%Z61-`JP9R#2STO5y9Q9;y zA~{vS*~Ff(qfF%7f|G=J1jZ)jP4KM(bHkI3N3#|Di8L31u>zm#`JZ2Zadc#O?4c!i z*20G*528>Z=94KNmw7mRvN3GJk9krsJ)5mCgBXQn`sInQ79t&iKxeB65okvKNC8{{5ZSn384&|@U}~m%W{{4k8SRRyN|qA z@XELZ*?(;umst29923Gu=C8pjB&|hQR@qJ`7TmFnhPK|pJO`tI6==q0rjjemi0=c< zj3QnE?cQu+IrFEU&9K?&dMpM(j6_mg$UdAxZtxcZ!|)HHfuSZ?0xV&L^oGtUYfi;v zxhOIo@zn_Tg}0SBpHlyy&0}5+e+f7~c1q61c5Svo%e*Cd1|JOA3gCF4SqWEXxykF#Wcu{aO#1s&Nz>yFp zLbw7pv+ko2%StoFL+z#jzQi=p2wr@I!`UUl?U)O9neDJU@gu~e5D#Hq3hctVtt-y; z4`y+efUGrhS$l#bXe=>189>4k=1qz^sg;6a%{_?Uogy=NucmgDk4P<_jk)GHw5~?Bi7yc27cQ#TD!a)Nw#ykjZ2 z8{S%c2W(R#SNwgwqaRV)Em|iD-otY!Yf7S=V2-+={+HSnu!$gu2d(_d;A2i{vX zLP%`Jx{V!sUJ_+H?KG~NU%u!(Ppq(pw3d0BuzSVLhh{5lW+A!znNK4p1?WdJY2Z5( zBP4rA;iLo#LpaPlgyP4*EQs*?n=ZT0za@zu!MBcBw@{a9uyOPT?^uL0v(rfM4#Rox z%0|bq75+KIzeKd_ADu-`#v!`AO98toE_xq2T}wiE+g&>ZtH4W#KL)tbb~u6f8gj4E z$ObrBiTg934rYg!i80(X_Ms$8M9~3%%c9S2Wt0PC^=5rk&O^v7Q9tAs!C0zcuxMwd@4RLCj||p29y% za%_twCte5sExR1SRzP_tZBrSH+u3R*04o!Bz96YEVn-+-i%RD)@%gYmfWNuzB&ppe z_u!nQKvpmTyM4xdIP;4XIz^GJb^`INnRLWMh_5q$1u%y-xXW|>JCIls(qpm&sZK(F6%>HbaLw8zhXtb$=SpD9U87X zEk1$CEOy#X>yeP2!uKg4+vkS={%t{Awg#ct2ql4clKic1x8nz!`G@}xO$DO8)HX2& z+>O63xDS6KbROvS-(n2?^4)Qqr4U;PPGHn^b;y^|_|78Qf#eJLW#hmmOqtUCq^??mw4}h7grtT)O&>@Ho~B5lO{*AdMVF zY%~JRiS1;*k+B4SQkwGEP1aG+7)kCWJ0-=g<9h?Hq~HPkXVHm`_E3HOR~7Pp7P}!N zL%as---ySc#m^8=vpxgL_L%5JiZ-QKIs6L{mpuiypi`J4lgZh~*v{NzYnX?{=dse{ zy6(Tu1S%mW3ufMpCN?p@Wy$$y#$#8BJ)wyqp?0U(DjIrW;sb~kBsZ!hb++d3!-;5G z&8b&JbI&Rvyw%5=PR{Y(_jT#Q}~t5B^q&6{3;Ltk1Kft?cxWi5F+>u@Z3R zl6%`68CkDpz1U7w?zgT#8E1sN1k+Kx7wcs-Bdd>KI_Aq6_lZBEfq@8ZC$^VH9$3N5 zG?SmT><=2<;mX=lECu|i*hkVu@Ggl>@O5Oq17B4HJywL| zImGVTE-E2mhwn$Nko%v5T47NpMrBVqgVjk z3;4pLF%MrlIQ{T7BEO;jWY`djmLlOhmzpW_1^_1u~I07VFH!qnl1! zLoedJ;m?6v44jWvR4eBDPA0ML}6XV%|2- zj8<_pjv+4V&%B)FZUO!9J%iJQQ4tOP*NS`C^(*WdS>m!rY@!#MUqD$t_%p~K={_c> zBfAMGI0j;9dreXv$ft2up`jyi!!kB-B4sGF7hf^kWoilzr-`S;Ixvp~wud{4cwhWg zL4Vs+9NSc6Js+GV5Q9SPEEvK(gApDx_d;k5f?;Sbt8rqmjz}yszA)@$0UVEwXP%pQ zWBhw)ASWXXYuP(SebyamXoa+>@0iCVFXK0Y$0_WKPgaPvY$szJ^LVZmKkGqq8Tj9A zorEUNu!-q#T2uTch3n$0%K99|JT})h^$?7OGbM4=ciB-Aev?=q;izCzym=tVB3R-8 zVw)&bh>@K_iD^VOkuinX2{cBKQ-h)z!St+UZ47VV%T4Y_;+@Hzq4$F}ljyNbR&XYS zqY%GP<(IKuekY#}~_V@-w78hEmwG(MGPI}nr2W?d3)KXlVFm#svv8S{Uxy6peA zkvHP_W7jF1E67*8#4;mzf$@;oX&U=$oJH2y8)Cumn^B}71yWm~C3ZUZ3|-avCpJ$Q zkS# zZ9Sg`!!y#MJ3drH&b}PoTO7;Tg>0S#@pWW<8o`_-$@*CQ5~Ag7-iW*ftdH7dn*%Sp zU5d^4>k$uNV>Qf|6z!*>n)LGg2x~19RH3mpUkq7RfR24B5RdVVF#@qGH1$!_r5thRn0WH5wTBu*ia5YhAC9k8|S+}#F`Wqh09 z%Kk8_!pmb#`cbq4@#Dlpm=~g02a3r2;mOwF`v}fpRAc;RJ=$sAHlzVra>#kDh}J(@ z_ohfbYi2OK%dCS~-=$z3MpA^Lfz64vv4$$6J0E|2)0LLT(%|0)Z$2DnR2H&+hV$%3 zOpWgaqO#WbX4-DPk~j*{JJwJd8azzH{XKCjcpLEm{LP8ifIAJ18{kXRjDc<)&+CEj zAq7Fcf-@Uf)=ovmx6)V!;_F|WgXHlz)fpjdpG=wrMe6x?M6##wXXETFibt@GQ7%I^)gB)NZ0 zZ=f~#PG36RVf2M`)&y%J5PVgwV-bs;jVEIrdB+eHr zG?Z9PIYQYU=837(3(X|%x=zP*7HuG(VR+G58%Sdi`4y_;&vr5mD0ajAE8xz+zm-Oo zkXwXU48#k7^$}lgIRjb8rbZ3hsQMX7&LecT^D8ti%gV0LL6jXrc%*yk`nNKEAB3N= zT1TQRHNHalx>z#}z#23$g(A-xT^X@>`VK=A`SHt+GVev5S>$cEQ@yMAgR)TYnRWXf zp#YKt@ed*?gAqG2e+^G|kgX=g_Gi0zS+qBQV>NRr3z>z2Ij%fuh9Uz}?13%8+e#gChV=Nk$l} zah2k+-3>S$DXp{C*3ov>?Gc{`rZu5FR=6n5RY&9t{zo(|YsQ$*X*I+bTe-x0npQ*d z-kW|DFbDami5I1rx!@oD3Ax=&zO&O~2+bz260wpH>o7k>(t2X67_tP!WRZx+BYwh~ z*ocPg59?GkGJwKWt@(4rp5SlDSVI0Tnp+2VApTSOelP(yifj_1PZ5=^V4jND8%XO} z*R&lSz<&er2Z(P*a4_*OR%n>=h&^LHmd49i)6#pSf3QGuLYuQ931cC4r?XXz*~}+f zv9HXlBlg0Qno(SKjiT{EqdAT+zscwUw-_2fEO5d$QwRSLa^Iqto!A`nU(?q=4{$Wb zQ4WCyBzx>QMQc*%9!a6CKmKBfjkh8-nRlXq>?6K)=68GC|L$dz#qke8@BcNfe<_kT z5qxeU*(p$ugykGzDdsPTO<~MoJ&9)OgO~8%ux3XR)5|(Zz)|EaL#G6sPQ*`I!HP6l zmv|B`buKuB9S6{g=EN!Mj%O{RfdsR=U5@j1O!W}-L7<5>SRQ{in_q@6TTgx-3NE8y z0)%(qe*$L+u`H|~ShKOotqkWP{+XaNgR;$@6WquMqS!tJ4nxRE@-P|;utr6#&3rWR zZt${GpFApNg-%yK$#uHw8bCJOSqb>#`iU4xj7` zBAeZ0$6V&J5sWNw@=$!M#k%3EMNTvd^aO{13u(+_b7`U{ITPStCGQIxs}+vx-xJYD z1hSB{jByO2H@J;)omdOV`56b9r=Zz>h~9ypk99`I6MV8pZg89-eI z^F|bD!Mqi9wty|s?2g_$@_kr4YeSOx;D`?SFT`#Lticxx-yudqh%#@QnhHM`>j;Q6 zXFdi0AWlb?+8WFSuM$Jnh=OI%2n&8g`#i&AvGw!dZ3#Xn=#N-f9v+<$UW(8ZFbN`& ziOEX2!BL&!rHS7#e+?6i2eu+-J9;M>%k0S8GxtUJF!@E$yNG`!^S?A5$e+-3lw~s1 zI&XvcE7mg*uYgzu5?Uf$llXjuP7yl|hG*o${{akbJ;;wsf$}&LupuwjvTfiF@D;vu zXe36r0rM43CNCI=Ak3ghdy;=b_zL+K;*Xho%#WrLS)>U zp7moyQcO zVXIf7=`*%^`A+Y#)hl_(sjXhCS=Ixj8{{X-+A{PJv diff --git a/netbox/translations/fr/LC_MESSAGES/django.po b/netbox/translations/fr/LC_MESSAGES/django.po index 374b65e7e..8c8a362ff 100644 --- a/netbox/translations/fr/LC_MESSAGES/django.po +++ b/netbox/translations/fr/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-21 19:48+0000\n" +"POT-Creation-Date: 2024-04-04 19:11+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2024\n" "Language-Team: French (https://app.transifex.com/netbox-community/teams/178115/fr/)\n" @@ -64,8 +64,8 @@ msgid "Your preferences have been updated." msgstr "Vos préférences ont été mises à jour." #: circuits/choices.py:21 dcim/choices.py:20 dcim/choices.py:102 -#: dcim/choices.py:174 dcim/choices.py:220 dcim/choices.py:1419 -#: dcim/choices.py:1495 dcim/choices.py:1545 virtualization/choices.py:20 +#: dcim/choices.py:174 dcim/choices.py:220 dcim/choices.py:1425 +#: dcim/choices.py:1501 dcim/choices.py:1551 virtualization/choices.py:20 #: virtualization/choices.py:45 vpn/choices.py:18 msgid "Planned" msgstr "Planifié" @@ -75,8 +75,8 @@ msgid "Provisioning" msgstr "Approvisionnement" #: circuits/choices.py:23 dcim/choices.py:22 dcim/choices.py:103 -#: dcim/choices.py:173 dcim/choices.py:219 dcim/choices.py:1494 -#: dcim/choices.py:1544 extras/tables/tables.py:380 ipam/choices.py:31 +#: dcim/choices.py:173 dcim/choices.py:219 dcim/choices.py:1500 +#: dcim/choices.py:1550 extras/tables/tables.py:380 ipam/choices.py:31 #: ipam/choices.py:49 ipam/choices.py:69 ipam/choices.py:154 #: templates/extras/configcontext.html:26 templates/users/user.html:38 #: users/forms/bulk_edit.py:36 virtualization/choices.py:22 @@ -85,7 +85,7 @@ msgid "Active" msgstr "Actif" #: circuits/choices.py:24 dcim/choices.py:172 dcim/choices.py:218 -#: dcim/choices.py:1493 dcim/choices.py:1546 virtualization/choices.py:24 +#: dcim/choices.py:1499 dcim/choices.py:1552 virtualization/choices.py:24 #: virtualization/choices.py:43 msgid "Offline" msgstr "Hors ligne" @@ -98,7 +98,7 @@ msgstr "Déprovisionnement" msgid "Decommissioned" msgstr "Mis hors service" -#: circuits/filtersets.py:29 circuits/filtersets.py:184 dcim/filtersets.py:124 +#: circuits/filtersets.py:29 circuits/filtersets.py:190 dcim/filtersets.py:124 #: dcim/filtersets.py:185 dcim/filtersets.py:260 dcim/filtersets.py:369 #: dcim/filtersets.py:903 dcim/filtersets.py:1207 dcim/filtersets.py:1702 #: dcim/filtersets.py:1945 dcim/filtersets.py:2003 ipam/filtersets.py:305 @@ -107,7 +107,7 @@ msgstr "Mis hors service" msgid "Region (ID)" msgstr "Région (ID)" -#: circuits/filtersets.py:36 circuits/filtersets.py:191 dcim/filtersets.py:130 +#: circuits/filtersets.py:36 circuits/filtersets.py:197 dcim/filtersets.py:130 #: dcim/filtersets.py:192 dcim/filtersets.py:267 dcim/filtersets.py:376 #: dcim/filtersets.py:910 dcim/filtersets.py:1214 dcim/filtersets.py:1709 #: dcim/filtersets.py:1952 dcim/filtersets.py:2010 extras/filtersets.py:414 @@ -117,7 +117,7 @@ msgstr "Région (ID)" msgid "Region (slug)" msgstr "Région (slug)" -#: circuits/filtersets.py:42 circuits/filtersets.py:197 dcim/filtersets.py:198 +#: circuits/filtersets.py:42 circuits/filtersets.py:203 dcim/filtersets.py:198 #: dcim/filtersets.py:273 dcim/filtersets.py:382 dcim/filtersets.py:916 #: dcim/filtersets.py:1220 dcim/filtersets.py:1715 dcim/filtersets.py:1958 #: dcim/filtersets.py:2016 ipam/filtersets.py:318 ipam/filtersets.py:909 @@ -125,7 +125,7 @@ msgstr "Région (slug)" msgid "Site group (ID)" msgstr "Groupe de sites (ID)" -#: circuits/filtersets.py:49 circuits/filtersets.py:204 dcim/filtersets.py:205 +#: circuits/filtersets.py:49 circuits/filtersets.py:210 dcim/filtersets.py:205 #: dcim/filtersets.py:280 dcim/filtersets.py:389 dcim/filtersets.py:923 #: dcim/filtersets.py:1227 dcim/filtersets.py:1722 dcim/filtersets.py:1965 #: dcim/filtersets.py:2023 extras/filtersets.py:420 ipam/filtersets.py:325 @@ -135,7 +135,7 @@ msgid "Site group (slug)" msgstr "Groupe de sites (slug)" #: circuits/filtersets.py:54 circuits/forms/bulk_import.py:117 -#: circuits/forms/filtersets.py:47 circuits/forms/filtersets.py:171 +#: circuits/forms/filtersets.py:47 circuits/forms/filtersets.py:167 #: circuits/forms/model_forms.py:137 dcim/forms/bulk_edit.py:166 #: dcim/forms/bulk_edit.py:238 dcim/forms/bulk_edit.py:570 #: dcim/forms/bulk_edit.py:763 dcim/forms/bulk_import.py:130 @@ -158,8 +158,8 @@ msgstr "Groupe de sites (slug)" #: ipam/forms/bulk_import.py:170 ipam/forms/bulk_import.py:437 #: ipam/forms/filtersets.py:152 ipam/forms/filtersets.py:226 #: ipam/forms/filtersets.py:417 ipam/forms/filtersets.py:470 -#: ipam/forms/model_forms.py:206 ipam/forms/model_forms.py:548 -#: ipam/forms/model_forms.py:640 ipam/tables/ip.py:244 +#: ipam/forms/model_forms.py:206 ipam/forms/model_forms.py:552 +#: ipam/forms/model_forms.py:644 ipam/tables/ip.py:244 #: ipam/tables/vlans.py:114 ipam/tables/vlans.py:216 #: templates/circuits/circuittermination_edit.html:20 #: templates/circuits/inc/circuit_termination.html:33 @@ -181,13 +181,13 @@ msgstr "Groupe de sites (slug)" #: virtualization/forms/model_forms.py:107 #: virtualization/forms/model_forms.py:174 #: virtualization/tables/clusters.py:77 -#: virtualization/tables/virtualmachines.py:53 vpn/forms/filtersets.py:262 +#: virtualization/tables/virtualmachines.py:62 vpn/forms/filtersets.py:262 #: wireless/forms/model_forms.py:77 wireless/forms/model_forms.py:117 msgid "Site" msgstr "Site" -#: circuits/filtersets.py:60 circuits/filtersets.py:215 -#: circuits/filtersets.py:252 dcim/filtersets.py:215 dcim/filtersets.py:290 +#: circuits/filtersets.py:60 circuits/filtersets.py:221 +#: circuits/filtersets.py:258 dcim/filtersets.py:215 dcim/filtersets.py:290 #: dcim/filtersets.py:363 extras/filtersets.py:436 ipam/filtersets.py:215 #: ipam/filtersets.py:335 ipam/filtersets.py:926 #: virtualization/filtersets.py:75 virtualization/filtersets.py:203 @@ -199,33 +199,39 @@ msgstr "Site (slug)" msgid "ASN (ID)" msgstr "ASN (ID)" -#: circuits/filtersets.py:87 circuits/filtersets.py:114 -#: circuits/filtersets.py:148 -msgid "Provider (ID)" -msgstr "Fournisseur (ID)" +#: circuits/filtersets.py:71 circuits/forms/filtersets.py:27 +#: ipam/forms/model_forms.py:158 ipam/models/asns.py:108 +#: ipam/models/asns.py:125 ipam/tables/asn.py:41 templates/ipam/asn.html:20 +msgid "ASN" +msgstr "ASN" #: circuits/filtersets.py:93 circuits/filtersets.py:120 #: circuits/filtersets.py:154 +msgid "Provider (ID)" +msgstr "Fournisseur (ID)" + +#: circuits/filtersets.py:99 circuits/filtersets.py:126 +#: circuits/filtersets.py:160 msgid "Provider (slug)" msgstr "Fournisseur (slug)" -#: circuits/filtersets.py:159 +#: circuits/filtersets.py:165 msgid "Provider account (ID)" msgstr "Compte fournisseur (ID)" -#: circuits/filtersets.py:164 +#: circuits/filtersets.py:170 msgid "Provider network (ID)" msgstr "Réseau fournisseur (ID)" -#: circuits/filtersets.py:168 +#: circuits/filtersets.py:174 msgid "Circuit type (ID)" msgstr "Type de circuit (ID)" -#: circuits/filtersets.py:174 +#: circuits/filtersets.py:180 msgid "Circuit type (slug)" msgstr "Type de circuit (slug)" -#: circuits/filtersets.py:209 circuits/filtersets.py:246 +#: circuits/filtersets.py:215 circuits/filtersets.py:252 #: dcim/filtersets.py:209 dcim/filtersets.py:284 dcim/filtersets.py:357 #: dcim/filtersets.py:927 dcim/filtersets.py:1232 dcim/filtersets.py:1727 #: dcim/filtersets.py:1969 dcim/filtersets.py:2028 ipam/filtersets.py:209 @@ -235,13 +241,13 @@ msgstr "Type de circuit (slug)" msgid "Site (ID)" msgstr "Site (ID)" -#: circuits/filtersets.py:238 core/filtersets.py:73 core/filtersets.py:132 +#: circuits/filtersets.py:244 core/filtersets.py:73 core/filtersets.py:132 #: dcim/filtersets.py:640 dcim/filtersets.py:1201 dcim/filtersets.py:2076 #: extras/filtersets.py:40 extras/filtersets.py:69 extras/filtersets.py:101 #: extras/filtersets.py:140 extras/filtersets.py:168 extras/filtersets.py:195 #: extras/filtersets.py:226 extras/filtersets.py:295 extras/filtersets.py:343 #: extras/filtersets.py:403 extras/filtersets.py:562 extras/filtersets.py:604 -#: extras/filtersets.py:645 ipam/forms/model_forms.py:430 +#: extras/filtersets.py:645 ipam/forms/model_forms.py:416 #: netbox/filtersets.py:275 netbox/forms/__init__.py:23 #: netbox/forms/base.py:163 templates/htmx/object_selector.html:28 #: templates/inc/filter_list.html:53 templates/ipam/ipaddress_assign.html:32 @@ -251,7 +257,7 @@ msgstr "Site (ID)" msgid "Search" msgstr "Rechercher" -#: circuits/filtersets.py:242 circuits/forms/bulk_edit.py:167 +#: circuits/filtersets.py:248 circuits/forms/bulk_edit.py:167 #: circuits/forms/model_forms.py:110 circuits/forms/model_forms.py:132 #: dcim/forms/connections.py:66 templates/circuits/circuit.html:15 #: templates/dcim/inc/cable_termination.html:55 @@ -259,11 +265,11 @@ msgstr "Rechercher" msgid "Circuit" msgstr "Circuit" -#: circuits/filtersets.py:256 +#: circuits/filtersets.py:262 msgid "ProviderNetwork (ID)" msgstr "Réseau fournisseur (ID)" -#: circuits/forms/bulk_edit.py:25 circuits/forms/filtersets.py:56 +#: circuits/forms/bulk_edit.py:25 circuits/forms/filtersets.py:52 #: circuits/forms/model_forms.py:26 circuits/tables/providers.py:33 #: dcim/forms/bulk_edit.py:126 dcim/forms/filtersets.py:187 #: dcim/forms/model_forms.py:126 dcim/tables/sites.py:94 @@ -374,8 +380,8 @@ msgstr "Description" #: circuits/forms/bulk_edit.py:46 circuits/forms/bulk_edit.py:68 #: circuits/forms/bulk_edit.py:118 circuits/forms/bulk_import.py:35 #: circuits/forms/bulk_import.py:50 circuits/forms/bulk_import.py:76 -#: circuits/forms/filtersets.py:70 circuits/forms/filtersets.py:88 -#: circuits/forms/filtersets.py:116 circuits/forms/filtersets.py:131 +#: circuits/forms/filtersets.py:66 circuits/forms/filtersets.py:84 +#: circuits/forms/filtersets.py:112 circuits/forms/filtersets.py:127 #: circuits/forms/model_forms.py:32 circuits/forms/model_forms.py:44 #: circuits/forms/model_forms.py:58 circuits/forms/model_forms.py:92 #: circuits/tables/circuits.py:55 circuits/tables/providers.py:72 @@ -387,18 +393,18 @@ msgstr "Description" msgid "Provider" msgstr "Prestataire" -#: circuits/forms/bulk_edit.py:75 circuits/forms/filtersets.py:91 +#: circuits/forms/bulk_edit.py:75 circuits/forms/filtersets.py:87 #: templates/circuits/providernetwork.html:31 msgid "Service ID" msgstr "Identifiant du service" -#: circuits/forms/bulk_edit.py:95 circuits/forms/filtersets.py:107 +#: circuits/forms/bulk_edit.py:95 circuits/forms/filtersets.py:103 #: dcim/forms/bulk_edit.py:204 dcim/forms/bulk_edit.py:500 #: dcim/forms/bulk_edit.py:694 dcim/forms/bulk_edit.py:1063 #: dcim/forms/bulk_edit.py:1090 dcim/forms/bulk_edit.py:1562 #: dcim/forms/filtersets.py:977 dcim/forms/filtersets.py:1353 -#: dcim/forms/filtersets.py:1374 dcim/tables/devices.py:722 -#: dcim/tables/devices.py:782 dcim/tables/devices.py:1009 +#: dcim/forms/filtersets.py:1374 dcim/tables/devices.py:726 +#: dcim/tables/devices.py:786 dcim/tables/devices.py:1013 #: dcim/tables/devicetypes.py:245 dcim/tables/devicetypes.py:260 #: dcim/tables/racks.py:32 extras/forms/bulk_edit.py:259 #: extras/tables/tables.py:328 templates/circuits/circuittype.html:33 @@ -410,7 +416,7 @@ msgid "Color" msgstr "Couleur" #: circuits/forms/bulk_edit.py:113 circuits/forms/bulk_import.py:89 -#: circuits/forms/filtersets.py:126 core/forms/bulk_edit.py:17 +#: circuits/forms/filtersets.py:122 core/forms/bulk_edit.py:17 #: core/forms/filtersets.py:29 core/tables/data.py:20 core/tables/jobs.py:18 #: dcim/forms/bulk_edit.py:281 dcim/forms/bulk_edit.py:672 #: dcim/forms/bulk_edit.py:811 dcim/forms/bulk_edit.py:879 @@ -429,7 +435,7 @@ msgstr "Couleur" #: dcim/forms/filtersets.py:1253 dcim/forms/filtersets.py:1348 #: dcim/forms/filtersets.py:1369 dcim/forms/object_import.py:89 #: dcim/forms/object_import.py:118 dcim/forms/object_import.py:150 -#: dcim/tables/devices.py:211 dcim/tables/devices.py:838 +#: dcim/tables/devices.py:211 dcim/tables/devices.py:842 #: dcim/tables/power.py:77 extras/forms/bulk_import.py:39 #: extras/tables/tables.py:278 extras/tables/tables.py:350 #: extras/tables/tables.py:448 netbox/tables/tables.py:234 @@ -454,12 +460,12 @@ msgid "Type" msgstr "Type" #: circuits/forms/bulk_edit.py:123 circuits/forms/bulk_import.py:82 -#: circuits/forms/filtersets.py:139 circuits/forms/model_forms.py:97 +#: circuits/forms/filtersets.py:135 circuits/forms/model_forms.py:97 msgid "Provider account" msgstr "Identifiant de compte du prestataire" #: circuits/forms/bulk_edit.py:131 circuits/forms/bulk_import.py:95 -#: circuits/forms/filtersets.py:150 core/forms/filtersets.py:34 +#: circuits/forms/filtersets.py:146 core/forms/filtersets.py:34 #: core/forms/filtersets.py:75 core/tables/data.py:23 core/tables/jobs.py:26 #: dcim/forms/bulk_edit.py:104 dcim/forms/bulk_edit.py:179 #: dcim/forms/bulk_edit.py:260 dcim/forms/bulk_edit.py:593 @@ -473,8 +479,8 @@ msgstr "Identifiant de compte du prestataire" #: dcim/forms/filtersets.py:281 dcim/forms/filtersets.py:726 #: dcim/forms/filtersets.py:835 dcim/forms/filtersets.py:871 #: dcim/forms/filtersets.py:972 dcim/forms/filtersets.py:1083 -#: dcim/tables/devices.py:173 dcim/tables/devices.py:841 -#: dcim/tables/devices.py:1069 dcim/tables/modules.py:69 +#: dcim/tables/devices.py:173 dcim/tables/devices.py:845 +#: dcim/tables/devices.py:1073 dcim/tables/modules.py:69 #: dcim/tables/power.py:74 dcim/tables/racks.py:66 dcim/tables/sites.py:82 #: dcim/tables/sites.py:133 ipam/forms/bulk_edit.py:240 #: ipam/forms/bulk_edit.py:289 ipam/forms/bulk_edit.py:337 @@ -482,7 +488,7 @@ msgstr "Identifiant de compte du prestataire" #: ipam/forms/bulk_import.py:256 ipam/forms/bulk_import.py:292 #: ipam/forms/bulk_import.py:458 ipam/forms/filtersets.py:205 #: ipam/forms/filtersets.py:270 ipam/forms/filtersets.py:341 -#: ipam/forms/filtersets.py:482 ipam/forms/model_forms.py:449 +#: ipam/forms/filtersets.py:482 ipam/forms/model_forms.py:435 #: ipam/tables/ip.py:236 ipam/tables/ip.py:309 ipam/tables/ip.py:359 #: ipam/tables/ip.py:421 ipam/tables/ip.py:448 ipam/tables/vlans.py:122 #: ipam/tables/vlans.py:227 templates/circuits/circuit.html:35 @@ -503,7 +509,7 @@ msgstr "Identifiant de compte du prestataire" #: virtualization/forms/bulk_import.py:80 #: virtualization/forms/filtersets.py:61 #: virtualization/forms/filtersets.py:156 virtualization/tables/clusters.py:74 -#: virtualization/tables/virtualmachines.py:50 vpn/forms/bulk_edit.py:38 +#: virtualization/tables/virtualmachines.py:59 vpn/forms/bulk_edit.py:38 #: vpn/forms/bulk_import.py:37 vpn/forms/filtersets.py:46 #: vpn/tables/tunnels.py:48 wireless/forms/bulk_edit.py:42 #: wireless/forms/bulk_edit.py:104 wireless/forms/bulk_import.py:43 @@ -514,7 +520,7 @@ msgid "Status" msgstr "Statut" #: circuits/forms/bulk_edit.py:137 circuits/forms/bulk_import.py:100 -#: circuits/forms/filtersets.py:119 dcim/forms/bulk_edit.py:120 +#: circuits/forms/filtersets.py:115 dcim/forms/bulk_edit.py:120 #: dcim/forms/bulk_edit.py:185 dcim/forms/bulk_edit.py:255 #: dcim/forms/bulk_edit.py:366 dcim/forms/bulk_edit.py:583 #: dcim/forms/bulk_edit.py:684 dcim/forms/bulk_edit.py:1590 @@ -572,15 +578,15 @@ msgstr "Statut" msgid "Tenant" msgstr "Locataire" -#: circuits/forms/bulk_edit.py:142 circuits/forms/filtersets.py:174 +#: circuits/forms/bulk_edit.py:142 circuits/forms/filtersets.py:170 msgid "Install date" msgstr "Date d'installation" -#: circuits/forms/bulk_edit.py:147 circuits/forms/filtersets.py:179 +#: circuits/forms/bulk_edit.py:147 circuits/forms/filtersets.py:175 msgid "Termination date" msgstr "Date de résiliation" -#: circuits/forms/bulk_edit.py:153 circuits/forms/filtersets.py:186 +#: circuits/forms/bulk_edit.py:153 circuits/forms/filtersets.py:182 msgid "Commit rate (Kbps)" msgstr "Débit engagé (Kbits/s)" @@ -613,7 +619,7 @@ msgstr "Prestataire assigné" #: circuits/forms/bulk_import.py:70 dcim/forms/bulk_import.py:178 #: dcim/forms/bulk_import.py:388 dcim/forms/bulk_import.py:1108 -#: dcim/forms/bulk_import.py:1187 extras/forms/bulk_import.py:229 +#: dcim/forms/bulk_import.py:1187 extras/forms/bulk_import.py:235 msgid "RGB color in hexadecimal. Example:" msgstr "Couleur RVB en hexadécimal. Exemple :" @@ -649,12 +655,12 @@ msgstr "État opérationnel" msgid "Assigned tenant" msgstr "Locataire assigné" -#: circuits/forms/bulk_import.py:123 circuits/forms/filtersets.py:147 +#: circuits/forms/bulk_import.py:123 circuits/forms/filtersets.py:143 #: circuits/forms/model_forms.py:143 msgid "Provider network" msgstr "Réseau de fournisseurs" -#: circuits/forms/filtersets.py:26 circuits/forms/filtersets.py:118 +#: circuits/forms/filtersets.py:26 circuits/forms/filtersets.py:114 #: dcim/forms/bulk_edit.py:247 dcim/forms/bulk_edit.py:345 #: dcim/forms/bulk_edit.py:575 dcim/forms/bulk_edit.py:622 #: dcim/forms/bulk_edit.py:772 dcim/forms/bulk_import.py:189 @@ -678,7 +684,7 @@ msgstr "Réseau de fournisseurs" #: extras/filtersets.py:441 extras/forms/filtersets.py:328 #: ipam/forms/bulk_edit.py:456 ipam/forms/filtersets.py:168 #: ipam/forms/filtersets.py:400 ipam/forms/filtersets.py:422 -#: ipam/forms/filtersets.py:448 ipam/forms/model_forms.py:560 +#: ipam/forms/filtersets.py:448 ipam/forms/model_forms.py:564 #: templates/dcim/device.html:26 templates/dcim/device_edit.html:30 #: templates/dcim/inc/cable_termination.html:12 #: templates/dcim/location.html:27 templates/dcim/powerpanel.html:27 @@ -688,13 +694,7 @@ msgstr "Réseau de fournisseurs" msgid "Location" msgstr "Emplacement" -#: circuits/forms/filtersets.py:27 ipam/forms/model_forms.py:158 -#: ipam/models/asns.py:108 ipam/models/asns.py:125 ipam/tables/asn.py:41 -#: templates/ipam/asn.html:20 -msgid "ASN" -msgstr "ASN" - -#: circuits/forms/filtersets.py:28 circuits/forms/filtersets.py:120 +#: circuits/forms/filtersets.py:28 circuits/forms/filtersets.py:116 #: dcim/forms/filtersets.py:136 dcim/forms/filtersets.py:150 #: dcim/forms/filtersets.py:166 dcim/forms/filtersets.py:198 #: dcim/forms/filtersets.py:249 dcim/forms/filtersets.py:334 @@ -707,7 +707,7 @@ msgstr "ASN" msgid "Contacts" msgstr "Contacts" -#: circuits/forms/filtersets.py:33 circuits/forms/filtersets.py:157 +#: circuits/forms/filtersets.py:33 circuits/forms/filtersets.py:153 #: dcim/forms/bulk_edit.py:110 dcim/forms/bulk_edit.py:222 #: dcim/forms/bulk_edit.py:747 dcim/forms/bulk_import.py:92 #: dcim/forms/filtersets.py:70 dcim/forms/filtersets.py:177 @@ -722,7 +722,7 @@ msgstr "Contacts" #: ipam/forms/bulk_edit.py:205 ipam/forms/bulk_edit.py:437 #: ipam/forms/bulk_edit.py:509 ipam/forms/filtersets.py:212 #: ipam/forms/filtersets.py:407 ipam/forms/filtersets.py:456 -#: ipam/forms/model_forms.py:532 templates/dcim/device.html:18 +#: ipam/forms/model_forms.py:536 templates/dcim/device.html:18 #: templates/dcim/rack.html:19 templates/dcim/rackreservation.html:25 #: templates/dcim/region.html:26 templates/dcim/site.html:31 #: templates/ipam/prefix.html:50 templates/ipam/vlan.html:19 @@ -732,7 +732,7 @@ msgstr "Contacts" msgid "Region" msgstr "Région" -#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:162 +#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:158 #: dcim/forms/bulk_edit.py:230 dcim/forms/bulk_edit.py:755 #: dcim/forms/filtersets.py:75 dcim/forms/filtersets.py:182 #: dcim/forms/filtersets.py:208 dcim/forms/filtersets.py:269 @@ -742,19 +742,15 @@ msgstr "Région" #: extras/filtersets.py:425 ipam/forms/bulk_edit.py:210 #: ipam/forms/bulk_edit.py:444 ipam/forms/bulk_edit.py:514 #: ipam/forms/filtersets.py:217 ipam/forms/filtersets.py:412 -#: ipam/forms/filtersets.py:461 ipam/forms/model_forms.py:545 +#: ipam/forms/filtersets.py:461 ipam/forms/model_forms.py:549 #: virtualization/forms/bulk_edit.py:85 virtualization/forms/filtersets.py:68 #: virtualization/forms/filtersets.py:134 #: virtualization/forms/model_forms.py:101 msgid "Site group" msgstr "Groupe de sites" -#: circuits/forms/filtersets.py:51 -msgid "ASN (legacy)" -msgstr "ASN (ancien)" - -#: circuits/forms/filtersets.py:65 circuits/forms/filtersets.py:83 -#: circuits/forms/filtersets.py:102 circuits/forms/filtersets.py:117 +#: circuits/forms/filtersets.py:61 circuits/forms/filtersets.py:79 +#: circuits/forms/filtersets.py:98 circuits/forms/filtersets.py:113 #: core/forms/filtersets.py:63 dcim/forms/bulk_edit.py:718 #: dcim/forms/filtersets.py:164 dcim/forms/filtersets.py:196 #: dcim/forms/filtersets.py:825 dcim/forms/filtersets.py:920 @@ -780,7 +776,7 @@ msgstr "ASN (ancien)" msgid "Attributes" msgstr "Attributs" -#: circuits/forms/filtersets.py:73 circuits/tables/circuits.py:60 +#: circuits/forms/filtersets.py:69 circuits/tables/circuits.py:60 #: circuits/tables/providers.py:66 templates/circuits/circuit.html:23 #: templates/circuits/provideraccount.html:25 msgid "Account" @@ -801,7 +797,7 @@ msgstr "Type de circuit" #: dcim/models/device_component_templates.py:491 #: dcim/models/device_component_templates.py:591 #: dcim/models/device_components.py:976 dcim/models/device_components.py:1050 -#: dcim/models/device_components.py:1166 dcim/models/devices.py:467 +#: dcim/models/device_components.py:1166 dcim/models/devices.py:469 #: dcim/models/racks.py:43 extras/models/tags.py:28 msgid "color" msgstr "couleur" @@ -823,8 +819,8 @@ msgid "Unique circuit ID" msgstr "ID de circuit unique" #: circuits/models/circuits.py:67 core/models/data.py:55 -#: core/models/jobs.py:85 dcim/models/cables.py:49 dcim/models/devices.py:641 -#: dcim/models/devices.py:1165 dcim/models/devices.py:1374 +#: core/models/jobs.py:85 dcim/models/cables.py:49 dcim/models/devices.py:643 +#: dcim/models/devices.py:1170 dcim/models/devices.py:1379 #: dcim/models/power.py:95 dcim/models/racks.py:97 dcim/models/sites.py:154 #: dcim/models/sites.py:266 ipam/models/ip.py:252 ipam/models/ip.py:521 #: ipam/models/ip.py:729 ipam/models/vlans.py:175 @@ -903,7 +899,7 @@ msgstr "ID du panneau de raccordement et numéro (s) de port" #: extras/models/models.py:541 extras/models/staging.py:31 #: extras/models/tags.py:32 netbox/models/__init__.py:109 #: netbox/models/__init__.py:144 netbox/models/__init__.py:190 -#: users/models.py:273 users/models.py:348 +#: users/models.py:274 users/models.py:353 #: virtualization/models/virtualmachines.py:282 msgid "description" msgstr "description" @@ -933,8 +929,8 @@ msgstr "" #: circuits/models/providers.py:22 circuits/models/providers.py:66 #: circuits/models/providers.py:104 core/models/data.py:42 #: core/models/jobs.py:46 dcim/models/device_component_templates.py:43 -#: dcim/models/device_components.py:54 dcim/models/devices.py:581 -#: dcim/models/devices.py:1305 dcim/models/devices.py:1370 +#: dcim/models/device_components.py:54 dcim/models/devices.py:583 +#: dcim/models/devices.py:1310 dcim/models/devices.py:1375 #: dcim/models/power.py:39 dcim/models/power.py:91 dcim/models/racks.py:62 #: dcim/models/sites.py:138 extras/models/configs.py:36 #: extras/models/configs.py:215 extras/models/customfields.py:89 @@ -947,7 +943,7 @@ msgstr "" #: ipam/models/vrfs.py:79 netbox/models/__init__.py:136 #: netbox/models/__init__.py:180 tenancy/models/contacts.py:64 #: tenancy/models/tenants.py:20 tenancy/models/tenants.py:45 -#: users/models.py:344 virtualization/models/clusters.py:57 +#: users/models.py:349 virtualization/models/clusters.py:57 #: virtualization/models/virtualmachines.py:70 #: virtualization/models/virtualmachines.py:272 vpn/models/crypto.py:24 #: vpn/models/crypto.py:71 vpn/models/crypto.py:131 vpn/models/crypto.py:183 @@ -1005,13 +1001,13 @@ msgstr "réseaux de fournisseurs" #: core/tables/data.py:16 core/tables/jobs.py:14 dcim/forms/filtersets.py:60 #: dcim/forms/object_create.py:42 dcim/tables/devices.py:88 #: dcim/tables/devices.py:125 dcim/tables/devices.py:167 -#: dcim/tables/devices.py:318 dcim/tables/devices.py:400 -#: dcim/tables/devices.py:444 dcim/tables/devices.py:496 -#: dcim/tables/devices.py:548 dcim/tables/devices.py:668 -#: dcim/tables/devices.py:749 dcim/tables/devices.py:799 -#: dcim/tables/devices.py:865 dcim/tables/devices.py:980 -#: dcim/tables/devices.py:1000 dcim/tables/devices.py:1029 -#: dcim/tables/devices.py:1059 dcim/tables/devicetypes.py:32 +#: dcim/tables/devices.py:322 dcim/tables/devices.py:404 +#: dcim/tables/devices.py:448 dcim/tables/devices.py:500 +#: dcim/tables/devices.py:552 dcim/tables/devices.py:672 +#: dcim/tables/devices.py:753 dcim/tables/devices.py:803 +#: dcim/tables/devices.py:869 dcim/tables/devices.py:984 +#: dcim/tables/devices.py:1004 dcim/tables/devices.py:1033 +#: dcim/tables/devices.py:1063 dcim/tables/devicetypes.py:32 #: dcim/tables/power.py:22 dcim/tables/power.py:62 dcim/tables/racks.py:23 #: dcim/tables/racks.py:53 dcim/tables/sites.py:24 dcim/tables/sites.py:51 #: dcim/tables/sites.py:78 dcim/tables/sites.py:125 @@ -1077,9 +1073,9 @@ msgstr "réseaux de fournisseurs" #: virtualization/forms/object_create.py:23 #: virtualization/tables/clusters.py:17 virtualization/tables/clusters.py:39 #: virtualization/tables/clusters.py:62 -#: virtualization/tables/virtualmachines.py:45 -#: virtualization/tables/virtualmachines.py:119 -#: virtualization/tables/virtualmachines.py:172 vpn/tables/crypto.py:18 +#: virtualization/tables/virtualmachines.py:54 +#: virtualization/tables/virtualmachines.py:132 +#: virtualization/tables/virtualmachines.py:185 vpn/tables/crypto.py:18 #: vpn/tables/crypto.py:57 vpn/tables/crypto.py:93 vpn/tables/crypto.py:129 #: vpn/tables/crypto.py:158 vpn/tables/l2vpn.py:23 vpn/tables/tunnels.py:18 #: vpn/tables/tunnels.py:40 wireless/tables/wirelesslan.py:18 @@ -1114,7 +1110,7 @@ msgstr "Taux d'engagement" #: circuits/tables/circuits.py:75 circuits/tables/providers.py:48 #: circuits/tables/providers.py:82 circuits/tables/providers.py:107 -#: dcim/tables/devices.py:1042 dcim/tables/devicetypes.py:92 +#: dcim/tables/devices.py:1046 dcim/tables/devicetypes.py:92 #: dcim/tables/modules.py:29 dcim/tables/modules.py:72 dcim/tables/power.py:39 #: dcim/tables/power.py:96 dcim/tables/racks.py:76 dcim/tables/racks.py:156 #: dcim/tables/sites.py:103 extras/forms/bulk_edit.py:320 @@ -1126,7 +1122,7 @@ msgstr "Taux d'engagement" #: templates/inc/panels/comments.html:6 tenancy/tables/contacts.py:68 #: tenancy/tables/tenants.py:46 utilities/forms/fields/fields.py:29 #: virtualization/tables/clusters.py:91 -#: virtualization/tables/virtualmachines.py:68 vpn/tables/crypto.py:37 +#: virtualization/tables/virtualmachines.py:81 vpn/tables/crypto.py:37 #: vpn/tables/crypto.py:74 vpn/tables/crypto.py:109 vpn/tables/crypto.py:140 #: vpn/tables/crypto.py:173 vpn/tables/l2vpn.py:37 vpn/tables/tunnels.py:61 #: wireless/tables/wirelesslan.py:27 wireless/tables/wirelesslan.py:58 @@ -1163,7 +1159,7 @@ msgid "Completed" msgstr "Terminé" #: core/choices.py:22 core/choices.py:59 dcim/choices.py:176 -#: dcim/choices.py:222 dcim/choices.py:1496 extras/choices.py:212 +#: dcim/choices.py:222 dcim/choices.py:1502 extras/choices.py:212 #: virtualization/choices.py:47 msgid "Failed" msgstr "Échoué" @@ -1245,7 +1241,7 @@ msgstr "Source de données (nom)" #: core/forms/bulk_edit.py:24 core/forms/filtersets.py:39 #: core/tables/data.py:26 dcim/forms/bulk_edit.py:1012 #: dcim/forms/bulk_edit.py:1285 dcim/forms/filtersets.py:1270 -#: dcim/tables/devices.py:573 dcim/tables/devicetypes.py:221 +#: dcim/tables/devices.py:577 dcim/tables/devicetypes.py:221 #: extras/forms/bulk_edit.py:97 extras/forms/bulk_edit.py:161 #: extras/forms/bulk_edit.py:220 extras/forms/filtersets.py:119 #: extras/forms/filtersets.py:206 extras/forms/filtersets.py:267 @@ -1384,7 +1380,7 @@ msgstr "" msgid "Rack Elevations" msgstr "Élévations des rayonnages" -#: core/forms/model_forms.py:148 dcim/choices.py:1407 +#: core/forms/model_forms.py:148 dcim/choices.py:1413 #: dcim/forms/bulk_edit.py:859 dcim/forms/bulk_edit.py:1242 #: dcim/forms/bulk_edit.py:1260 dcim/tables/racks.py:89 #: netbox/navigation/menu.py:276 netbox/navigation/menu.py:280 @@ -1446,7 +1442,7 @@ msgstr " (par défaut)" #: core/models/config.py:18 core/models/data.py:282 core/models/files.py:27 #: core/models/jobs.py:50 extras/models/models.py:760 -#: netbox/models/features.py:52 users/models.py:248 +#: netbox/models/features.py:52 users/models.py:249 msgid "created" msgstr "créé" @@ -1504,7 +1500,7 @@ msgstr "URL" #: core/models/data.py:62 dcim/models/device_component_templates.py:392 #: dcim/models/device_components.py:513 extras/models/models.py:88 -#: extras/models/models.py:331 extras/models/models.py:556 users/models.py:353 +#: extras/models/models.py:331 extras/models/models.py:556 users/models.py:358 msgid "enabled" msgstr "activé" @@ -1723,7 +1719,7 @@ msgid "Staging" msgstr "Mise en scène" #: dcim/choices.py:23 dcim/choices.py:178 dcim/choices.py:223 -#: dcim/choices.py:1420 virtualization/choices.py:23 +#: dcim/choices.py:1426 virtualization/choices.py:23 #: virtualization/choices.py:48 msgid "Decommissioning" msgstr "Démantèlement" @@ -1783,7 +1779,7 @@ msgstr "Obsolète" msgid "Millimeters" msgstr "Millimètres" -#: dcim/choices.py:115 dcim/choices.py:1442 +#: dcim/choices.py:115 dcim/choices.py:1448 msgid "Inches" msgstr "Pouces" @@ -1795,8 +1791,8 @@ msgstr "Pouces" #: dcim/forms/filtersets.py:226 dcim/forms/model_forms.py:73 #: dcim/forms/model_forms.py:94 dcim/forms/model_forms.py:172 #: dcim/forms/model_forms.py:962 dcim/forms/model_forms.py:1303 -#: dcim/forms/object_import.py:181 dcim/tables/devices.py:676 -#: dcim/tables/devices.py:960 extras/tables/tables.py:181 +#: dcim/forms/object_import.py:181 dcim/tables/devices.py:680 +#: dcim/tables/devices.py:964 extras/tables/tables.py:181 #: ipam/tables/fhrp.py:59 ipam/tables/ip.py:374 ipam/tables/services.py:44 #: templates/dcim/interface.html:105 templates/dcim/interface.html:321 #: templates/dcim/location.html:44 templates/dcim/region.html:38 @@ -1809,7 +1805,7 @@ msgstr "Pouces" #: tenancy/forms/bulk_import.py:58 tenancy/forms/model_forms.py:24 #: tenancy/forms/model_forms.py:69 virtualization/forms/bulk_edit.py:206 #: virtualization/forms/bulk_import.py:151 -#: virtualization/tables/virtualmachines.py:142 wireless/forms/bulk_edit.py:23 +#: virtualization/tables/virtualmachines.py:155 wireless/forms/bulk_edit.py:23 #: wireless/forms/bulk_import.py:21 wireless/forms/model_forms.py:20 msgid "Parent" msgstr "Parent" @@ -1858,7 +1854,7 @@ msgstr "De droite à gauche" msgid "Side to rear" msgstr "D'un côté à l'arrière" -#: dcim/choices.py:198 dcim/choices.py:1215 +#: dcim/choices.py:198 dcim/choices.py:1221 msgid "Passive" msgstr "Passif" @@ -1886,8 +1882,8 @@ msgstr "International/ITA" msgid "Proprietary" msgstr "Propriétaire" -#: dcim/choices.py:534 dcim/choices.py:764 dcim/choices.py:1131 -#: dcim/choices.py:1133 dcim/choices.py:1338 dcim/choices.py:1340 +#: dcim/choices.py:534 dcim/choices.py:764 dcim/choices.py:1137 +#: dcim/choices.py:1139 dcim/choices.py:1344 dcim/choices.py:1346 #: netbox/navigation/menu.py:188 msgid "Other" msgstr "Autres" @@ -1900,177 +1896,177 @@ msgstr "ITA/International" msgid "Physical" msgstr "Physique" -#: dcim/choices.py:795 dcim/choices.py:949 +#: dcim/choices.py:795 dcim/choices.py:952 msgid "Virtual" msgstr "Virtuel" -#: dcim/choices.py:796 dcim/choices.py:1019 dcim/forms/bulk_edit.py:1398 +#: dcim/choices.py:796 dcim/choices.py:1022 dcim/forms/bulk_edit.py:1398 #: dcim/forms/filtersets.py:1233 dcim/forms/model_forms.py:888 #: dcim/forms/model_forms.py:1197 netbox/navigation/menu.py:128 #: netbox/navigation/menu.py:132 templates/dcim/interface.html:217 msgid "Wireless" msgstr "Sans fil" -#: dcim/choices.py:947 +#: dcim/choices.py:950 msgid "Virtual interfaces" msgstr "Interfaces virtuelles" -#: dcim/choices.py:950 dcim/forms/bulk_edit.py:1295 +#: dcim/choices.py:953 dcim/forms/bulk_edit.py:1295 #: dcim/forms/bulk_import.py:785 dcim/forms/model_forms.py:876 -#: dcim/tables/devices.py:680 templates/dcim/interface.html:109 +#: dcim/tables/devices.py:684 templates/dcim/interface.html:109 #: templates/virtualization/vminterface.html:46 #: virtualization/forms/bulk_edit.py:211 #: virtualization/forms/bulk_import.py:158 -#: virtualization/tables/virtualmachines.py:146 +#: virtualization/tables/virtualmachines.py:159 msgid "Bridge" msgstr "Passerelle" -#: dcim/choices.py:951 +#: dcim/choices.py:954 msgid "Link Aggregation Group (LAG)" msgstr "Groupe d'agrégation de liens (LAG)" -#: dcim/choices.py:955 +#: dcim/choices.py:958 msgid "Ethernet (fixed)" msgstr "Ethernet (fixe)" -#: dcim/choices.py:969 +#: dcim/choices.py:972 msgid "Ethernet (modular)" msgstr "Ethernet (modulaire)" -#: dcim/choices.py:1005 +#: dcim/choices.py:1008 msgid "Ethernet (backplane)" msgstr "Ethernet (panneau arrière)" -#: dcim/choices.py:1033 +#: dcim/choices.py:1036 msgid "Cellular" msgstr "Cellulaire" -#: dcim/choices.py:1080 dcim/forms/filtersets.py:302 +#: dcim/choices.py:1086 dcim/forms/filtersets.py:302 #: dcim/forms/filtersets.py:736 dcim/forms/filtersets.py:876 #: dcim/forms/filtersets.py:1426 templates/dcim/inventoryitem.html:53 #: templates/dcim/virtualchassis_edit.html:55 msgid "Serial" msgstr "Série" -#: dcim/choices.py:1095 +#: dcim/choices.py:1101 msgid "Coaxial" msgstr "Coaxiale" -#: dcim/choices.py:1112 +#: dcim/choices.py:1118 msgid "Stacking" msgstr "Empilage" -#: dcim/choices.py:1162 +#: dcim/choices.py:1168 msgid "Half" msgstr "La moitié" -#: dcim/choices.py:1163 +#: dcim/choices.py:1169 msgid "Full" msgstr "Complet" -#: dcim/choices.py:1164 netbox/preferences.py:29 wireless/choices.py:480 +#: dcim/choices.py:1170 netbox/preferences.py:29 wireless/choices.py:480 msgid "Auto" msgstr "Automatique" -#: dcim/choices.py:1175 +#: dcim/choices.py:1181 msgid "Access" msgstr "Accès" -#: dcim/choices.py:1176 ipam/tables/vlans.py:168 ipam/tables/vlans.py:213 +#: dcim/choices.py:1182 ipam/tables/vlans.py:168 ipam/tables/vlans.py:213 #: templates/dcim/inc/interface_vlans_table.html:7 msgid "Tagged" msgstr "Tagué" -#: dcim/choices.py:1177 +#: dcim/choices.py:1183 msgid "Tagged (All)" msgstr "Tagué (Tous)" -#: dcim/choices.py:1206 +#: dcim/choices.py:1212 msgid "IEEE Standard" msgstr "Norme IEEE" -#: dcim/choices.py:1217 +#: dcim/choices.py:1223 msgid "Passive 24V (2-pair)" msgstr "24 V passif (2 paires)" -#: dcim/choices.py:1218 +#: dcim/choices.py:1224 msgid "Passive 24V (4-pair)" msgstr "24 V passif (4 paires)" -#: dcim/choices.py:1219 +#: dcim/choices.py:1225 msgid "Passive 48V (2-pair)" msgstr "48 V passif (2 paires)" -#: dcim/choices.py:1220 +#: dcim/choices.py:1226 msgid "Passive 48V (4-pair)" msgstr "48 V passif (4 paires)" -#: dcim/choices.py:1282 dcim/choices.py:1378 +#: dcim/choices.py:1288 dcim/choices.py:1384 msgid "Copper" msgstr "Cuivre" -#: dcim/choices.py:1305 +#: dcim/choices.py:1311 msgid "Fiber Optic" msgstr "fibre optique" -#: dcim/choices.py:1394 +#: dcim/choices.py:1400 msgid "Fiber" msgstr "Fibre" -#: dcim/choices.py:1418 dcim/forms/filtersets.py:1140 +#: dcim/choices.py:1424 dcim/forms/filtersets.py:1140 msgid "Connected" msgstr "Connecté" -#: dcim/choices.py:1437 +#: dcim/choices.py:1443 msgid "Kilometers" msgstr "Kilomètres" -#: dcim/choices.py:1438 templates/dcim/cable_trace.html:62 +#: dcim/choices.py:1444 templates/dcim/cable_trace.html:62 msgid "Meters" msgstr "Compteurs" -#: dcim/choices.py:1439 +#: dcim/choices.py:1445 msgid "Centimeters" msgstr "Centimètres" -#: dcim/choices.py:1440 +#: dcim/choices.py:1446 msgid "Miles" msgstr "Miles" -#: dcim/choices.py:1441 templates/dcim/cable_trace.html:63 +#: dcim/choices.py:1447 templates/dcim/cable_trace.html:63 msgid "Feet" msgstr "Pieds" -#: dcim/choices.py:1457 templates/dcim/device.html:332 +#: dcim/choices.py:1463 templates/dcim/device.html:332 #: templates/dcim/rack.html:157 msgid "Kilograms" msgstr "Kilogrammes" -#: dcim/choices.py:1458 +#: dcim/choices.py:1464 msgid "Grams" msgstr "Grammes" -#: dcim/choices.py:1459 templates/dcim/rack.html:158 +#: dcim/choices.py:1465 templates/dcim/rack.html:158 msgid "Pounds" msgstr "Livres" -#: dcim/choices.py:1460 +#: dcim/choices.py:1466 msgid "Ounces" msgstr "Onces" -#: dcim/choices.py:1506 tenancy/choices.py:17 +#: dcim/choices.py:1512 tenancy/choices.py:17 msgid "Primary" msgstr "Primaire" -#: dcim/choices.py:1507 +#: dcim/choices.py:1513 msgid "Redundant" msgstr "Redondant" -#: dcim/choices.py:1528 +#: dcim/choices.py:1534 msgid "Single phase" msgstr "Monophasé" -#: dcim/choices.py:1529 +#: dcim/choices.py:1535 msgid "Three-phase" msgstr "Triphasé" @@ -2347,7 +2343,7 @@ msgid "Virtual Chassis (ID)" msgstr "Châssis virtuel (ID)" #: dcim/filtersets.py:1303 dcim/forms/filtersets.py:106 -#: dcim/tables/devices.py:235 netbox/navigation/menu.py:67 +#: dcim/tables/devices.py:239 netbox/navigation/menu.py:67 #: templates/dcim/device.html:123 templates/dcim/device_edit.html:93 #: templates/dcim/virtualchassis.html:20 #: templates/dcim/virtualchassis_add.html:8 @@ -2371,7 +2367,7 @@ msgstr "VID attribué" #: dcim/filtersets.py:1448 dcim/forms/bulk_edit.py:1374 #: dcim/forms/bulk_import.py:836 dcim/forms/filtersets.py:1328 #: dcim/forms/model_forms.py:1182 dcim/models/device_components.py:712 -#: dcim/tables/devices.py:642 ipam/filtersets.py:282 ipam/filtersets.py:293 +#: dcim/tables/devices.py:646 ipam/filtersets.py:282 ipam/filtersets.py:293 #: ipam/filtersets.py:449 ipam/filtersets.py:550 ipam/filtersets.py:561 #: ipam/forms/bulk_edit.py:226 ipam/forms/bulk_edit.py:281 #: ipam/forms/bulk_edit.py:323 ipam/forms/bulk_import.py:156 @@ -2379,8 +2375,8 @@ msgstr "VID attribué" #: ipam/forms/filtersets.py:66 ipam/forms/filtersets.py:167 #: ipam/forms/filtersets.py:295 ipam/forms/model_forms.py:59 #: ipam/forms/model_forms.py:203 ipam/forms/model_forms.py:246 -#: ipam/forms/model_forms.py:290 ipam/forms/model_forms.py:412 -#: ipam/forms/model_forms.py:426 ipam/forms/model_forms.py:440 +#: ipam/forms/model_forms.py:290 ipam/forms/model_forms.py:398 +#: ipam/forms/model_forms.py:412 ipam/forms/model_forms.py:426 #: ipam/models/ip.py:232 ipam/models/ip.py:511 ipam/models/ip.py:719 #: ipam/models/vrfs.py:62 ipam/tables/ip.py:241 ipam/tables/ip.py:306 #: ipam/tables/ip.py:356 ipam/tables/ip.py:445 @@ -2393,7 +2389,7 @@ msgstr "VID attribué" #: virtualization/forms/filtersets.py:220 #: virtualization/forms/model_forms.py:347 #: virtualization/models/virtualmachines.py:348 -#: virtualization/tables/virtualmachines.py:123 +#: virtualization/tables/virtualmachines.py:136 msgid "VRF" msgstr "VRF" @@ -2407,7 +2403,7 @@ msgid "L2VPN (ID)" msgstr "L2VPN (IDENTIFIANT)" #: dcim/filtersets.py:1465 dcim/forms/filtersets.py:1333 -#: dcim/tables/devices.py:590 ipam/filtersets.py:973 +#: dcim/tables/devices.py:594 ipam/filtersets.py:973 #: ipam/forms/filtersets.py:499 ipam/tables/vlans.py:133 #: templates/dcim/interface.html:94 templates/ipam/vlan.html:69 #: templates/vpn/l2vpntermination.html:15 @@ -2478,7 +2474,7 @@ msgstr "Balises" #: dcim/forms/bulk_create.py:112 dcim/forms/filtersets.py:1390 #: dcim/forms/model_forms.py:426 dcim/forms/model_forms.py:475 #: dcim/forms/object_create.py:196 dcim/forms/object_create.py:352 -#: dcim/tables/devices.py:198 dcim/tables/devices.py:725 +#: dcim/tables/devices.py:198 dcim/tables/devices.py:729 #: dcim/tables/devicetypes.py:242 templates/dcim/device.html:45 #: templates/dcim/device.html:129 templates/dcim/modulebay.html:35 #: templates/dcim/virtualchassis.html:59 @@ -2497,7 +2493,7 @@ msgstr "" #: dcim/forms/bulk_edit.py:115 dcim/forms/bulk_import.py:99 #: dcim/forms/model_forms.py:120 dcim/tables/sites.py:89 #: ipam/filtersets.py:936 ipam/forms/bulk_edit.py:528 -#: ipam/forms/bulk_import.py:444 ipam/forms/model_forms.py:509 +#: ipam/forms/bulk_import.py:444 ipam/forms/model_forms.py:495 #: ipam/tables/fhrp.py:67 ipam/tables/vlans.py:118 ipam/tables/vlans.py:221 #: templates/dcim/interface.html:294 templates/dcim/site.html:37 #: templates/ipam/inc/panels/fhrp_groups.html:10 templates/ipam/vlan.html:30 @@ -2547,8 +2543,8 @@ msgstr "Fuseau horaire" #: dcim/forms/filtersets.py:704 dcim/forms/filtersets.py:1417 #: dcim/forms/model_forms.py:224 dcim/forms/model_forms.py:970 #: dcim/forms/model_forms.py:1311 dcim/forms/object_import.py:186 -#: dcim/tables/devices.py:202 dcim/tables/devices.py:833 -#: dcim/tables/devices.py:944 dcim/tables/devicetypes.py:300 +#: dcim/tables/devices.py:202 dcim/tables/devices.py:837 +#: dcim/tables/devices.py:948 dcim/tables/devicetypes.py:300 #: dcim/tables/racks.py:69 extras/filtersets.py:457 #: ipam/forms/bulk_edit.py:245 ipam/forms/bulk_edit.py:294 #: ipam/forms/bulk_edit.py:342 ipam/forms/bulk_edit.py:546 @@ -2557,7 +2553,7 @@ msgstr "Fuseau horaire" #: ipam/forms/filtersets.py:232 ipam/forms/filtersets.py:278 #: ipam/forms/filtersets.py:346 ipam/forms/filtersets.py:490 #: ipam/forms/model_forms.py:187 ipam/forms/model_forms.py:222 -#: ipam/forms/model_forms.py:249 ipam/forms/model_forms.py:647 +#: ipam/forms/model_forms.py:249 ipam/forms/model_forms.py:651 #: ipam/tables/ip.py:257 ipam/tables/ip.py:313 ipam/tables/ip.py:363 #: ipam/tables/vlans.py:126 ipam/tables/vlans.py:230 #: templates/dcim/device.html:187 @@ -2575,7 +2571,7 @@ msgstr "Fuseau horaire" #: virtualization/forms/bulk_import.py:106 #: virtualization/forms/filtersets.py:153 #: virtualization/forms/model_forms.py:198 -#: virtualization/tables/virtualmachines.py:65 vpn/forms/bulk_edit.py:86 +#: virtualization/tables/virtualmachines.py:74 vpn/forms/bulk_edit.py:86 #: vpn/forms/bulk_import.py:81 vpn/forms/filtersets.py:84 #: vpn/forms/model_forms.py:77 vpn/forms/model_forms.py:112 #: vpn/tables/tunnels.py:82 @@ -2669,7 +2665,7 @@ msgstr "Unité de poids" #: dcim/forms/model_forms.py:669 dcim/forms/object_create.py:399 #: dcim/tables/devices.py:194 dcim/tables/power.py:70 dcim/tables/racks.py:148 #: ipam/forms/bulk_edit.py:464 ipam/forms/filtersets.py:427 -#: ipam/forms/model_forms.py:571 templates/dcim/device.html:30 +#: ipam/forms/model_forms.py:575 templates/dcim/device.html:30 #: templates/dcim/inc/cable_termination.html:16 #: templates/dcim/powerfeed.html:31 templates/dcim/rack.html:14 #: templates/dcim/rack/base.html:4 templates/dcim/rack_edit.html:8 @@ -2702,7 +2698,7 @@ msgstr "Matériel" #: dcim/forms/model_forms.py:334 dcim/forms/model_forms.py:374 #: dcim/forms/model_forms.py:975 dcim/forms/model_forms.py:1316 #: dcim/forms/object_import.py:192 dcim/tables/devices.py:129 -#: dcim/tables/devices.py:205 dcim/tables/devices.py:947 +#: dcim/tables/devices.py:205 dcim/tables/devices.py:951 #: dcim/tables/devicetypes.py:81 dcim/tables/devicetypes.py:304 #: dcim/tables/modules.py:20 dcim/tables/modules.py:60 #: templates/dcim/devicetype.html:17 templates/dcim/inventoryitem.html:45 @@ -2749,7 +2745,7 @@ msgstr "Type d'appareil" msgid "Module Type" msgstr "Type de module" -#: dcim/forms/bulk_edit.py:506 dcim/models/devices.py:472 +#: dcim/forms/bulk_edit.py:506 dcim/models/devices.py:474 msgid "VM role" msgstr "rôle de machine virtuelle" @@ -2781,13 +2777,15 @@ msgstr "Rôle de l'appareil" #: dcim/forms/bulk_edit.py:588 dcim/forms/bulk_import.py:443 #: dcim/forms/filtersets.py:723 dcim/forms/model_forms.py:389 -#: dcim/forms/model_forms.py:448 extras/filtersets.py:468 -#: templates/dcim/device.html:191 templates/dcim/platform.html:27 +#: dcim/forms/model_forms.py:448 dcim/tables/devices.py:215 +#: extras/filtersets.py:468 templates/dcim/device.html:191 +#: templates/dcim/platform.html:27 #: templates/virtualization/virtualmachine.html:30 #: virtualization/forms/bulk_edit.py:159 #: virtualization/forms/bulk_import.py:122 #: virtualization/forms/filtersets.py:164 #: virtualization/forms/model_forms.py:206 +#: virtualization/tables/virtualmachines.py:78 msgid "Platform" msgstr "Plateforme" @@ -2811,16 +2809,16 @@ msgstr "Plateforme" #: dcim/forms/model_forms.py:760 dcim/forms/model_forms.py:1011 #: dcim/forms/model_forms.py:1460 dcim/forms/object_create.py:256 #: dcim/tables/connections.py:22 dcim/tables/connections.py:41 -#: dcim/tables/connections.py:60 dcim/tables/devices.py:314 -#: dcim/tables/devices.py:379 dcim/tables/devices.py:423 -#: dcim/tables/devices.py:468 dcim/tables/devices.py:522 -#: dcim/tables/devices.py:614 dcim/tables/devices.py:715 -#: dcim/tables/devices.py:775 dcim/tables/devices.py:825 -#: dcim/tables/devices.py:885 dcim/tables/devices.py:937 -#: dcim/tables/devices.py:1063 dcim/tables/modules.py:52 +#: dcim/tables/connections.py:60 dcim/tables/devices.py:318 +#: dcim/tables/devices.py:383 dcim/tables/devices.py:427 +#: dcim/tables/devices.py:472 dcim/tables/devices.py:526 +#: dcim/tables/devices.py:618 dcim/tables/devices.py:719 +#: dcim/tables/devices.py:779 dcim/tables/devices.py:829 +#: dcim/tables/devices.py:889 dcim/tables/devices.py:941 +#: dcim/tables/devices.py:1067 dcim/tables/modules.py:52 #: extras/forms/filtersets.py:329 ipam/forms/bulk_import.py:303 #: ipam/forms/bulk_import.py:489 ipam/forms/filtersets.py:532 -#: ipam/forms/model_forms.py:685 ipam/tables/vlans.py:176 +#: ipam/forms/model_forms.py:689 ipam/tables/vlans.py:176 #: templates/dcim/consoleport.html:23 templates/dcim/consoleserverport.html:23 #: templates/dcim/device.html:14 templates/dcim/device.html:128 #: templates/dcim/device_edit.html:10 templates/dcim/devicebay.html:23 @@ -2842,7 +2840,7 @@ msgstr "Plateforme" #: virtualization/forms/bulk_import.py:99 #: virtualization/forms/filtersets.py:124 #: virtualization/forms/model_forms.py:188 -#: virtualization/tables/virtualmachines.py:61 vpn/choices.py:44 +#: virtualization/tables/virtualmachines.py:70 vpn/choices.py:44 #: vpn/forms/bulk_import.py:86 vpn/forms/bulk_import.py:283 #: vpn/forms/filtersets.py:271 vpn/forms/model_forms.py:89 #: vpn/forms/model_forms.py:124 vpn/forms/model_forms.py:237 @@ -2982,7 +2980,7 @@ msgid "Wireless role" msgstr "Rôle sans fil" #: dcim/forms/bulk_edit.py:1178 dcim/forms/model_forms.py:595 -#: dcim/forms/model_forms.py:1026 dcim/tables/devices.py:337 +#: dcim/forms/model_forms.py:1026 dcim/tables/devices.py:341 #: templates/dcim/consoleport.html:27 templates/dcim/consoleserverport.html:27 #: templates/dcim/frontport.html:27 templates/dcim/interface.html:35 #: templates/dcim/module.html:51 templates/dcim/modulebay.html:57 @@ -2991,7 +2989,7 @@ msgstr "Rôle sans fil" msgid "Module" msgstr "Modules" -#: dcim/forms/bulk_edit.py:1305 dcim/tables/devices.py:685 +#: dcim/forms/bulk_edit.py:1305 dcim/tables/devices.py:689 #: templates/dcim/interface.html:113 msgid "LAG" msgstr "DÉCALAGE" @@ -3003,7 +3001,7 @@ msgstr "Contextes des appareils virtuels" #: dcim/forms/bulk_edit.py:1316 dcim/forms/bulk_import.py:659 #: dcim/forms/bulk_import.py:685 dcim/forms/filtersets.py:1163 #: dcim/forms/filtersets.py:1185 dcim/forms/filtersets.py:1258 -#: dcim/tables/devices.py:626 +#: dcim/tables/devices.py:630 #: templates/circuits/inc/circuit_termination.html:94 #: templates/dcim/consoleport.html:43 templates/dcim/consoleserverport.html:43 msgid "Speed" @@ -3028,13 +3026,13 @@ msgid "VLAN group" msgstr "groupe VLAN" #: dcim/forms/bulk_edit.py:1361 dcim/forms/model_forms.py:1164 -#: dcim/tables/devices.py:599 virtualization/forms/bulk_edit.py:247 +#: dcim/tables/devices.py:603 virtualization/forms/bulk_edit.py:247 #: virtualization/forms/model_forms.py:329 msgid "Untagged VLAN" msgstr "VLAN non balisé" #: dcim/forms/bulk_edit.py:1369 dcim/forms/model_forms.py:1173 -#: dcim/tables/devices.py:605 virtualization/forms/bulk_edit.py:255 +#: dcim/tables/devices.py:609 virtualization/forms/bulk_edit.py:255 #: virtualization/forms/model_forms.py:338 msgid "Tagged VLANs" msgstr "VLAN balisés" @@ -3044,7 +3042,7 @@ msgid "Wireless LAN group" msgstr "Groupe LAN sans fil" #: dcim/forms/bulk_edit.py:1384 dcim/forms/model_forms.py:1151 -#: dcim/tables/devices.py:635 netbox/navigation/menu.py:134 +#: dcim/tables/devices.py:639 netbox/navigation/menu.py:134 #: templates/dcim/interface.html:289 wireless/tables/wirelesslan.py:24 msgid "Wireless LANs" msgstr "Réseaux locaux sans fil" @@ -3216,9 +3214,9 @@ msgid "Virtual chassis" msgstr "Châssis virtuel" #: dcim/forms/bulk_import.py:462 dcim/forms/model_forms.py:457 -#: dcim/tables/devices.py:231 extras/filtersets.py:501 +#: dcim/tables/devices.py:235 extras/filtersets.py:501 #: extras/forms/filtersets.py:330 ipam/forms/bulk_edit.py:478 -#: ipam/forms/model_forms.py:588 templates/dcim/device.html:239 +#: ipam/forms/model_forms.py:592 templates/dcim/device.html:239 #: templates/virtualization/cluster.html:11 #: templates/virtualization/virtualmachine.html:92 #: templates/virtualization/virtualmachine.html:102 @@ -3230,7 +3228,7 @@ msgstr "Châssis virtuel" #: virtualization/forms/filtersets.py:196 #: virtualization/forms/model_forms.py:82 #: virtualization/forms/model_forms.py:179 -#: virtualization/tables/virtualmachines.py:57 +#: virtualization/tables/virtualmachines.py:66 msgid "Cluster" msgstr "Cluster" @@ -3412,7 +3410,7 @@ msgstr "Port arrière correspondant" msgid "Physical medium classification" msgstr "Classification des supports physiques" -#: dcim/forms/bulk_import.py:973 dcim/tables/devices.py:846 +#: dcim/forms/bulk_import.py:973 dcim/tables/devices.py:850 msgid "Installed device" msgstr "Appareil installé" @@ -3500,7 +3498,7 @@ msgid "{side_upper} side termination not found: {device} {name}" msgstr "{side_upper} terminaison latérale introuvable : {device} {name}" #: dcim/forms/bulk_import.py:1244 dcim/forms/model_forms.py:696 -#: dcim/tables/devices.py:1033 templates/dcim/device.html:130 +#: dcim/tables/devices.py:1037 templates/dcim/device.html:130 #: templates/dcim/virtualchassis.html:28 templates/dcim/virtualchassis.html:60 msgid "Master" msgstr "Maître" @@ -3627,7 +3625,7 @@ msgstr "Occupé" #: dcim/forms/filtersets.py:1155 dcim/forms/filtersets.py:1177 #: dcim/forms/filtersets.py:1199 dcim/forms/filtersets.py:1216 -#: dcim/forms/filtersets.py:1236 dcim/tables/devices.py:372 +#: dcim/forms/filtersets.py:1236 dcim/tables/devices.py:376 #: templates/dcim/consoleport.html:59 templates/dcim/consoleserverport.html:59 #: templates/dcim/frontport.html:74 templates/dcim/interface.html:146 #: templates/dcim/powerfeed.html:118 templates/dcim/poweroutlet.html:63 @@ -3641,7 +3639,7 @@ msgid "Virtual Device Context" msgstr "Contexte du périphérique virtuel" #: dcim/forms/filtersets.py:1248 extras/forms/bulk_edit.py:315 -#: extras/forms/bulk_import.py:239 extras/forms/filtersets.py:479 +#: extras/forms/bulk_import.py:245 extras/forms/filtersets.py:479 #: extras/forms/model_forms.py:557 extras/tables/tables.py:487 #: templates/extras/journalentry.html:33 msgid "Kind" @@ -3673,7 +3671,7 @@ msgid "Transmit power (dBm)" msgstr "Puissance de transmission (dBm)" #: dcim/forms/filtersets.py:1344 dcim/forms/filtersets.py:1366 -#: dcim/tables/devices.py:344 templates/dcim/cable.html:12 +#: dcim/tables/devices.py:348 templates/dcim/cable.html:12 #: templates/dcim/cable_edit.html:46 templates/dcim/cable_trace.html:43 #: templates/dcim/frontport.html:84 #: templates/dcim/inc/connection_endpoints.html:4 @@ -3681,7 +3679,7 @@ msgstr "Puissance de transmission (dBm)" msgid "Cable" msgstr "câble" -#: dcim/forms/filtersets.py:1434 dcim/tables/devices.py:956 +#: dcim/forms/filtersets.py:1434 dcim/tables/devices.py:960 msgid "Discovered" msgstr "Découvert" @@ -3728,7 +3726,7 @@ msgstr "Châssis" msgid "Device Role" msgstr "Rôle de l'appareil" -#: dcim/forms/model_forms.py:428 dcim/models/devices.py:632 +#: dcim/forms/model_forms.py:428 dcim/models/devices.py:634 msgid "The lowest-numbered unit occupied by the device" msgstr "L'unité la moins numérotée occupée par l'appareil" @@ -3780,9 +3778,7 @@ msgstr "Interface LAG" #: templates/wireless/wirelesslink.html:10 #: templates/wireless/wirelesslink.html:49 #: virtualization/forms/model_forms.py:351 vpn/forms/bulk_import.py:297 -#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 -#: vpn/forms/model_forms.py:241 vpn/forms/model_forms.py:436 -#: vpn/forms/model_forms.py:445 vpn/tables/tunnels.py:91 +#: vpn/forms/model_forms.py:436 vpn/forms/model_forms.py:445 #: wireless/forms/model_forms.py:112 wireless/forms/model_forms.py:152 msgid "Interface" msgstr "Interface" @@ -3859,7 +3855,7 @@ msgstr "" "sont attendus." #: dcim/forms/object_create.py:109 dcim/forms/object_create.py:270 -#: dcim/tables/devices.py:281 +#: dcim/tables/devices.py:285 msgid "Rear ports" msgstr "Ports arrière" @@ -3897,7 +3893,7 @@ msgstr "" "Le nombre de ports frontaux à créer ({frontport_count}) doit correspondre au" " nombre sélectionné de positions des ports arrière ({rearport_count})." -#: dcim/forms/object_create.py:408 dcim/tables/devices.py:1039 +#: dcim/forms/object_create.py:408 dcim/tables/devices.py:1043 #: ipam/tables/fhrp.py:31 templates/dcim/virtualchassis.html:54 #: templates/dcim/virtualchassis_edit.html:48 templates/ipam/fhrpgroup.html:39 msgid "Members" @@ -4643,13 +4639,13 @@ msgstr "rôle des articles d'inventaire" msgid "inventory item roles" msgstr "rôles des articles d'inventaire" -#: dcim/models/device_components.py:1230 dcim/models/devices.py:595 -#: dcim/models/devices.py:1173 dcim/models/racks.py:113 +#: dcim/models/device_components.py:1230 dcim/models/devices.py:597 +#: dcim/models/devices.py:1178 dcim/models/racks.py:113 msgid "serial number" msgstr "numéro de série" -#: dcim/models/device_components.py:1238 dcim/models/devices.py:603 -#: dcim/models/devices.py:1180 dcim/models/racks.py:120 +#: dcim/models/device_components.py:1238 dcim/models/devices.py:605 +#: dcim/models/devices.py:1185 dcim/models/racks.py:120 msgid "asset tag" msgstr "étiquette d'actif" @@ -4699,7 +4695,7 @@ msgstr "fabricant" msgid "manufacturers" msgstr "fabricants" -#: dcim/models/devices.py:82 dcim/models/devices.py:381 +#: dcim/models/devices.py:82 dcim/models/devices.py:382 msgid "model" msgstr "modèle" @@ -4707,11 +4703,11 @@ msgstr "modèle" msgid "default platform" msgstr "plateforme par défaut" -#: dcim/models/devices.py:98 dcim/models/devices.py:385 +#: dcim/models/devices.py:98 dcim/models/devices.py:386 msgid "part number" msgstr "numéro de pièce" -#: dcim/models/devices.py:101 dcim/models/devices.py:388 +#: dcim/models/devices.py:101 dcim/models/devices.py:389 msgid "Discrete part number (optional)" msgstr "Numéro de pièce discret (facultatif)" @@ -4749,7 +4745,7 @@ msgstr "" "pour appareils. Laissez ce champ vide si ce type d'appareil n'est ni un " "parent ni un enfant." -#: dcim/models/devices.py:128 dcim/models/devices.py:647 +#: dcim/models/devices.py:128 dcim/models/devices.py:649 msgid "airflow" msgstr "débit d'air" @@ -4761,12 +4757,12 @@ msgstr "type d'appareil" msgid "device types" msgstr "types d'appareils" -#: dcim/models/devices.py:289 +#: dcim/models/devices.py:290 msgid "U height must be in increments of 0.5 rack units." msgstr "" "La hauteur en U doit être exprimée par incréments de 0,5 unité de rack." -#: dcim/models/devices.py:306 +#: dcim/models/devices.py:307 #, python-brace-format msgid "" "Device {device} in rack {rack} does not have sufficient space to accommodate" @@ -4775,7 +4771,7 @@ msgstr "" "Appareil {device} en rack {rack} ne dispose pas de suffisamment d'espace " "pour accueillir une hauteur de {height}U" -#: dcim/models/devices.py:321 +#: dcim/models/devices.py:322 #, python-brace-format msgid "" "Unable to set 0U height: Found {racked_instance_count} " @@ -4785,7 +4781,7 @@ msgstr "" "href=\"{url}\">{racked_instance_count} les instances déjà monté dans des" " racks." -#: dcim/models/devices.py:330 +#: dcim/models/devices.py:331 msgid "" "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." @@ -4793,153 +4789,153 @@ msgstr "" "Vous devez supprimer tous les modèles de baies d'appareils associés à cet " "appareil avant de le déclassifier en tant qu'appareil parent." -#: dcim/models/devices.py:336 +#: dcim/models/devices.py:337 msgid "Child device types must be 0U." msgstr "Les types d'appareils pour enfants doivent être 0U." -#: dcim/models/devices.py:404 +#: dcim/models/devices.py:405 msgid "module type" msgstr "type de module" -#: dcim/models/devices.py:405 +#: dcim/models/devices.py:406 msgid "module types" msgstr "types de modules" -#: dcim/models/devices.py:473 +#: dcim/models/devices.py:475 msgid "Virtual machines may be assigned to this role" msgstr "Des machines virtuelles peuvent être affectées à ce rôle" -#: dcim/models/devices.py:485 +#: dcim/models/devices.py:487 msgid "device role" msgstr "rôle de l'appareil" -#: dcim/models/devices.py:486 +#: dcim/models/devices.py:488 msgid "device roles" msgstr "rôles des appareils" -#: dcim/models/devices.py:503 +#: dcim/models/devices.py:505 msgid "Optionally limit this platform to devices of a certain manufacturer" msgstr "" "Limitez éventuellement cette plate-forme aux appareils d'un certain " "fabricant" -#: dcim/models/devices.py:515 +#: dcim/models/devices.py:517 msgid "platform" msgstr "plateforme" -#: dcim/models/devices.py:516 +#: dcim/models/devices.py:518 msgid "platforms" msgstr "plateformes" -#: dcim/models/devices.py:564 +#: dcim/models/devices.py:566 msgid "The function this device serves" msgstr "La fonction de cet appareil" -#: dcim/models/devices.py:596 +#: dcim/models/devices.py:598 msgid "Chassis serial number, assigned by the manufacturer" msgstr "Numéro de série du châssis, attribué par le fabricant" -#: dcim/models/devices.py:604 dcim/models/devices.py:1181 +#: dcim/models/devices.py:606 dcim/models/devices.py:1186 msgid "A unique tag used to identify this device" msgstr "Un tag unique utilisé pour identifier cet appareil" -#: dcim/models/devices.py:631 +#: dcim/models/devices.py:633 msgid "position (U)" msgstr "position (U)" -#: dcim/models/devices.py:638 +#: dcim/models/devices.py:640 msgid "rack face" msgstr "face du rack" -#: dcim/models/devices.py:658 dcim/models/devices.py:1390 +#: dcim/models/devices.py:660 dcim/models/devices.py:1395 #: virtualization/models/virtualmachines.py:98 msgid "primary IPv4" msgstr "IPv4 principal" -#: dcim/models/devices.py:666 dcim/models/devices.py:1398 +#: dcim/models/devices.py:668 dcim/models/devices.py:1403 #: virtualization/models/virtualmachines.py:106 msgid "primary IPv6" msgstr "IPv6 principal" -#: dcim/models/devices.py:674 +#: dcim/models/devices.py:676 msgid "out-of-band IP" msgstr "IP hors bande" -#: dcim/models/devices.py:691 +#: dcim/models/devices.py:693 msgid "VC position" msgstr "Position en VC" -#: dcim/models/devices.py:695 +#: dcim/models/devices.py:697 msgid "Virtual chassis position" msgstr "Position virtuelle du châssis" -#: dcim/models/devices.py:698 +#: dcim/models/devices.py:700 msgid "VC priority" msgstr "Priorité VC" -#: dcim/models/devices.py:702 +#: dcim/models/devices.py:704 msgid "Virtual chassis master election priority" msgstr "Priorité d'élection principale du châssis virtuel" -#: dcim/models/devices.py:705 dcim/models/sites.py:207 +#: dcim/models/devices.py:707 dcim/models/sites.py:207 msgid "latitude" msgstr "latitude" -#: dcim/models/devices.py:710 dcim/models/devices.py:718 +#: dcim/models/devices.py:712 dcim/models/devices.py:720 #: dcim/models/sites.py:212 dcim/models/sites.py:220 msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgstr "Coordonnées GPS au format décimal (xx.yyyyyy)" -#: dcim/models/devices.py:713 dcim/models/sites.py:215 +#: dcim/models/devices.py:715 dcim/models/sites.py:215 msgid "longitude" msgstr "longitude" -#: dcim/models/devices.py:786 +#: dcim/models/devices.py:788 msgid "Device name must be unique per site." msgstr "Le nom de l'appareil doit être unique par site." -#: dcim/models/devices.py:797 ipam/models/services.py:75 +#: dcim/models/devices.py:799 ipam/models/services.py:75 msgid "device" msgstr "appareil" -#: dcim/models/devices.py:798 +#: dcim/models/devices.py:800 msgid "devices" msgstr "appareils" -#: dcim/models/devices.py:838 +#: dcim/models/devices.py:840 #, python-brace-format msgid "Rack {rack} does not belong to site {site}." msgstr "Étagère {rack} n'appartient pas au site {site}." -#: dcim/models/devices.py:843 +#: dcim/models/devices.py:845 #, python-brace-format msgid "Location {location} does not belong to site {site}." msgstr "Emplacement {location} n'appartient pas au site {site}." -#: dcim/models/devices.py:849 +#: dcim/models/devices.py:851 #, python-brace-format msgid "Rack {rack} does not belong to location {location}." msgstr "Étagère {rack} n'appartient pas au lieu {location}." -#: dcim/models/devices.py:856 +#: dcim/models/devices.py:858 msgid "Cannot select a rack face without assigning a rack." msgstr "Impossible de sélectionner une face de rack sans attribuer un rack." -#: dcim/models/devices.py:860 +#: dcim/models/devices.py:862 msgid "Cannot select a rack position without assigning a rack." msgstr "" "Impossible de sélectionner une position de rack sans attribuer un rack." -#: dcim/models/devices.py:866 +#: dcim/models/devices.py:868 msgid "Position must be in increments of 0.5 rack units." msgstr "La position doit être exprimée par incréments de 0,5 unité de rack." -#: dcim/models/devices.py:870 +#: dcim/models/devices.py:872 msgid "Must specify rack face when defining rack position." msgstr "" "Doit spécifier la face du rack lors de la définition de la position du rack." -#: dcim/models/devices.py:878 +#: dcim/models/devices.py:880 #, python-brace-format msgid "" "A 0U device type ({device_type}) cannot be assigned to a rack position." @@ -4947,7 +4943,7 @@ msgstr "" "Un appareil de type 0U ({device_type}) ne peut pas être attribué à une " "position de rack." -#: dcim/models/devices.py:889 +#: dcim/models/devices.py:891 msgid "" "Child device types cannot be assigned to a rack face. This is an attribute " "of the parent device." @@ -4955,7 +4951,7 @@ msgstr "" "Les types d'appareils pour enfants ne peuvent pas être attribués à une face " "de rack. Il s'agit d'un attribut de l'appareil parent." -#: dcim/models/devices.py:896 +#: dcim/models/devices.py:898 msgid "" "Child device types cannot be assigned to a rack position. This is an " "attribute of the parent device." @@ -4963,7 +4959,7 @@ msgstr "" "Les types d'appareils pour enfants ne peuvent pas être affectés à une " "position en rack. Il s'agit d'un attribut de l'appareil parent." -#: dcim/models/devices.py:910 +#: dcim/models/devices.py:912 #, python-brace-format msgid "" "U{position} is already occupied or does not have sufficient space to " @@ -4972,22 +4968,22 @@ msgstr "" "U{position} est déjà occupé ou ne dispose pas de suffisamment d'espace pour " "accueillir ce type d'appareil : {device_type} ({u_height}U)" -#: dcim/models/devices.py:925 +#: dcim/models/devices.py:927 #, python-brace-format msgid "{ip} is not an IPv4 address." msgstr "{ip} n'est pas une adresse IPv4." -#: dcim/models/devices.py:934 dcim/models/devices.py:949 +#: dcim/models/devices.py:936 dcim/models/devices.py:951 #, python-brace-format msgid "The specified IP address ({ip}) is not assigned to this device." msgstr "L'adresse IP spécifiée ({ip}) n'est pas attribué à cet appareil." -#: dcim/models/devices.py:940 +#: dcim/models/devices.py:942 #, python-brace-format msgid "{ip} is not an IPv6 address." msgstr "{ip} n'est pas une adresse IPv6." -#: dcim/models/devices.py:967 +#: dcim/models/devices.py:969 #, python-brace-format msgid "" "The assigned platform is limited to {platform_manufacturer} device types, " @@ -4997,25 +4993,25 @@ msgstr "" "d'appareils, mais le type de cet appareil appartient à " "{devicetype_manufacturer}." -#: dcim/models/devices.py:978 +#: dcim/models/devices.py:980 #, python-brace-format msgid "The assigned cluster belongs to a different site ({site})" msgstr "Le cluster attribué appartient à un autre site ({site})" -#: dcim/models/devices.py:986 +#: dcim/models/devices.py:988 msgid "A device assigned to a virtual chassis must have its position defined." msgstr "" "La position d'un appareil affecté à un châssis virtuel doit être définie." -#: dcim/models/devices.py:1188 +#: dcim/models/devices.py:1193 msgid "module" msgstr "module" -#: dcim/models/devices.py:1189 +#: dcim/models/devices.py:1194 msgid "modules" msgstr "modules" -#: dcim/models/devices.py:1205 +#: dcim/models/devices.py:1210 #, python-brace-format msgid "" "Module must be installed within a module bay belonging to the assigned " @@ -5024,22 +5020,22 @@ msgstr "" "Le module doit être installé dans une baie de modules appartenant au " "périphérique attribué ({device})." -#: dcim/models/devices.py:1309 +#: dcim/models/devices.py:1314 msgid "domain" msgstr "domaine" -#: dcim/models/devices.py:1322 dcim/models/devices.py:1323 +#: dcim/models/devices.py:1327 dcim/models/devices.py:1328 msgid "virtual chassis" msgstr "châssis virtuel" -#: dcim/models/devices.py:1338 +#: dcim/models/devices.py:1343 #, python-brace-format msgid "" "The selected master ({master}) is not assigned to this virtual chassis." msgstr "" "Le master sélectionné ({master}) n'est pas attribué à ce châssis virtuel." -#: dcim/models/devices.py:1354 +#: dcim/models/devices.py:1359 #, python-brace-format msgid "" "Unable to delete virtual chassis {self}. There are member interfaces which " @@ -5048,33 +5044,33 @@ msgstr "" "Impossible de supprimer le châssis virtuel {self}. Il existe des interfaces " "membres qui forment des interfaces LAG inter-châssis." -#: dcim/models/devices.py:1379 vpn/models/l2vpn.py:37 +#: dcim/models/devices.py:1384 vpn/models/l2vpn.py:37 msgid "identifier" msgstr "identificateur" -#: dcim/models/devices.py:1380 +#: dcim/models/devices.py:1385 msgid "Numeric identifier unique to the parent device" msgstr "Identifiant numérique propre à l'appareil parent" -#: dcim/models/devices.py:1408 extras/models/models.py:129 +#: dcim/models/devices.py:1413 extras/models/models.py:129 #: extras/models/models.py:724 netbox/models/__init__.py:114 msgid "comments" msgstr "commentaires" -#: dcim/models/devices.py:1424 +#: dcim/models/devices.py:1429 msgid "virtual device context" msgstr "contexte du périphérique virtuel" -#: dcim/models/devices.py:1425 +#: dcim/models/devices.py:1430 msgid "virtual device contexts" msgstr "contextes de périphériques virtuels" -#: dcim/models/devices.py:1457 +#: dcim/models/devices.py:1462 #, python-brace-format msgid "{ip} is not an IPv{family} address." msgstr "{ip} n'est pas un IPV{family} adresse." -#: dcim/models/devices.py:1463 +#: dcim/models/devices.py:1468 msgid "Primary IP address must belong to an interface on the assigned device." msgstr "" "L'adresse IP principale doit appartenir à une interface sur l'appareil " @@ -5465,7 +5461,7 @@ msgstr "Port de console" msgid "Reachable" msgstr "Joignable" -#: dcim/tables/connections.py:46 dcim/tables/devices.py:529 +#: dcim/tables/connections.py:46 dcim/tables/devices.py:533 #: templates/dcim/inventoryitem_edit.html:64 #: templates/dcim/poweroutlet.html:47 templates/dcim/powerport.html:18 msgid "Power Port" @@ -5484,7 +5480,7 @@ msgstr "Appareils" msgid "VMs" msgstr "machines virtuelles" -#: dcim/tables/devices.py:133 dcim/tables/devices.py:245 +#: dcim/tables/devices.py:133 dcim/tables/devices.py:249 #: extras/forms/model_forms.py:515 templates/dcim/device.html:114 #: templates/dcim/device/render_config.html:11 #: templates/dcim/device/render_config.html:15 @@ -5493,62 +5489,62 @@ msgstr "machines virtuelles" #: templates/virtualization/virtualmachine.html:47 #: templates/virtualization/virtualmachine/render_config.html:11 #: templates/virtualization/virtualmachine/render_config.html:15 -#: virtualization/tables/virtualmachines.py:93 +#: virtualization/tables/virtualmachines.py:106 msgid "Config Template" msgstr "Modèle de configuration" -#: dcim/tables/devices.py:216 dcim/tables/devices.py:1074 +#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 #: ipam/forms/bulk_import.py:511 ipam/forms/model_forms.py:296 #: ipam/tables/ip.py:352 ipam/tables/ip.py:418 ipam/tables/ip.py:441 #: templates/ipam/ipaddress.html:12 templates/ipam/ipaddress_edit.html:14 -#: virtualization/tables/virtualmachines.py:81 +#: virtualization/tables/virtualmachines.py:94 msgid "IP Address" msgstr "Adresse IP" -#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 -#: virtualization/tables/virtualmachines.py:72 +#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 +#: virtualization/tables/virtualmachines.py:85 msgid "IPv4 Address" msgstr "Adresse IPv4" -#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 -#: virtualization/tables/virtualmachines.py:76 +#: dcim/tables/devices.py:228 dcim/tables/devices.py:1086 +#: virtualization/tables/virtualmachines.py:89 msgid "IPv6 Address" msgstr "Adresse IPv6" -#: dcim/tables/devices.py:239 +#: dcim/tables/devices.py:243 msgid "VC Position" msgstr "Position en VC" -#: dcim/tables/devices.py:242 +#: dcim/tables/devices.py:246 msgid "VC Priority" msgstr "Priorité VC" -#: dcim/tables/devices.py:249 templates/dcim/device_edit.html:38 +#: dcim/tables/devices.py:253 templates/dcim/device_edit.html:38 #: templates/dcim/devicebay_populate.html:16 msgid "Parent Device" msgstr "Appareil parent" -#: dcim/tables/devices.py:254 +#: dcim/tables/devices.py:258 msgid "Position (Device Bay)" msgstr "Position (baie de l'appareil)" -#: dcim/tables/devices.py:263 +#: dcim/tables/devices.py:267 msgid "Console ports" msgstr "Ports de console" -#: dcim/tables/devices.py:266 +#: dcim/tables/devices.py:270 msgid "Console server ports" msgstr "Ports du serveur de consoles" -#: dcim/tables/devices.py:269 +#: dcim/tables/devices.py:273 msgid "Power ports" msgstr "Ports d'alimentation" -#: dcim/tables/devices.py:272 +#: dcim/tables/devices.py:276 msgid "Power outlets" msgstr "Prises de courant" -#: dcim/tables/devices.py:275 dcim/tables/devices.py:1087 +#: dcim/tables/devices.py:279 dcim/tables/devices.py:1091 #: dcim/tables/devicetypes.py:125 dcim/views.py:1005 dcim/views.py:1244 #: dcim/views.py:1930 netbox/navigation/menu.py:82 #: netbox/navigation/menu.py:238 templates/dcim/device/base.html:37 @@ -5558,53 +5554,53 @@ msgstr "Prises de courant" #: templates/dcim/virtualdevicecontext.html:85 #: templates/virtualization/virtualmachine/base.html:27 #: templates/virtualization/virtualmachine_list.html:14 -#: virtualization/tables/virtualmachines.py:87 virtualization/views.py:368 +#: virtualization/tables/virtualmachines.py:100 virtualization/views.py:368 #: wireless/tables/wirelesslan.py:55 msgid "Interfaces" msgstr "Interfaces" -#: dcim/tables/devices.py:278 +#: dcim/tables/devices.py:282 msgid "Front ports" msgstr "Ports avant" -#: dcim/tables/devices.py:284 +#: dcim/tables/devices.py:288 msgid "Device bays" msgstr "Baies pour appareils" -#: dcim/tables/devices.py:287 +#: dcim/tables/devices.py:291 msgid "Module bays" msgstr "Baies pour modules" -#: dcim/tables/devices.py:290 +#: dcim/tables/devices.py:294 msgid "Inventory items" msgstr "Articles d'inventaire" -#: dcim/tables/devices.py:329 dcim/tables/modules.py:56 +#: dcim/tables/devices.py:333 dcim/tables/modules.py:56 #: templates/dcim/modulebay.html:17 msgid "Module Bay" msgstr "Module Bay" -#: dcim/tables/devices.py:350 +#: dcim/tables/devices.py:354 msgid "Cable Color" msgstr "Couleur du câble" -#: dcim/tables/devices.py:356 +#: dcim/tables/devices.py:360 msgid "Link Peers" msgstr "Lier les pairs" -#: dcim/tables/devices.py:359 +#: dcim/tables/devices.py:363 msgid "Mark Connected" msgstr "Marquer comme connecté" -#: dcim/tables/devices.py:475 +#: dcim/tables/devices.py:479 msgid "Maximum draw (W)" msgstr "Tirage maximal (W)" -#: dcim/tables/devices.py:478 +#: dcim/tables/devices.py:482 msgid "Allocated draw (W)" msgstr "Tirage alloué (W)" -#: dcim/tables/devices.py:578 ipam/forms/model_forms.py:707 +#: dcim/tables/devices.py:582 ipam/forms/model_forms.py:711 #: ipam/tables/fhrp.py:28 ipam/views.py:597 ipam/views.py:691 #: netbox/navigation/menu.py:146 netbox/navigation/menu.py:148 #: templates/dcim/interface.html:351 templates/ipam/ipaddress_bulk_add.html:15 @@ -5613,12 +5609,12 @@ msgstr "Tirage alloué (W)" msgid "IP Addresses" msgstr "Adresses IP" -#: dcim/tables/devices.py:584 netbox/navigation/menu.py:190 +#: dcim/tables/devices.py:588 netbox/navigation/menu.py:190 #: templates/ipam/inc/panels/fhrp_groups.html:5 msgid "FHRP Groups" msgstr "Groupes FHRP" -#: dcim/tables/devices.py:596 templates/dcim/interface.html:90 +#: dcim/tables/devices.py:600 templates/dcim/interface.html:90 #: templates/virtualization/vminterface.html:70 templates/vpn/tunnel.html:18 #: templates/vpn/tunneltermination.html:14 vpn/forms/bulk_edit.py:75 #: vpn/forms/bulk_import.py:76 vpn/forms/filtersets.py:41 @@ -5627,20 +5623,20 @@ msgstr "Groupes FHRP" msgid "Tunnel" msgstr "Tunnel" -#: dcim/tables/devices.py:621 dcim/tables/devicetypes.py:224 +#: dcim/tables/devices.py:625 dcim/tables/devicetypes.py:224 #: templates/dcim/interface.html:66 msgid "Management Only" msgstr "Gestion uniquement" -#: dcim/tables/devices.py:629 +#: dcim/tables/devices.py:633 msgid "Wireless link" msgstr "Liaison sans fil" -#: dcim/tables/devices.py:639 +#: dcim/tables/devices.py:643 msgid "VDCs" msgstr "VDC" -#: dcim/tables/devices.py:647 dcim/tables/devicetypes.py:48 +#: dcim/tables/devices.py:651 dcim/tables/devicetypes.py:48 #: dcim/tables/devicetypes.py:140 dcim/views.py:1080 dcim/views.py:2023 #: netbox/navigation/menu.py:91 templates/dcim/device/base.html:52 #: templates/dcim/device_list.html:71 templates/dcim/devicetype/base.html:49 @@ -5649,7 +5645,7 @@ msgstr "VDC" msgid "Inventory Items" msgstr "Articles d'inventaire" -#: dcim/tables/devices.py:728 +#: dcim/tables/devices.py:732 #: templates/circuits/inc/circuit_termination.html:80 #: templates/dcim/consoleport.html:81 templates/dcim/consoleserverport.html:81 #: templates/dcim/frontport.html:53 templates/dcim/frontport.html:125 @@ -5658,28 +5654,28 @@ msgstr "Articles d'inventaire" msgid "Rear Port" msgstr "Port arrière" -#: dcim/tables/devices.py:893 templates/dcim/modulebay.html:51 +#: dcim/tables/devices.py:897 templates/dcim/modulebay.html:51 msgid "Installed Module" msgstr "Module installé" -#: dcim/tables/devices.py:896 +#: dcim/tables/devices.py:900 msgid "Module Serial" msgstr "Série du module" -#: dcim/tables/devices.py:900 +#: dcim/tables/devices.py:904 msgid "Module Asset Tag" msgstr "Étiquette d'actif du module" -#: dcim/tables/devices.py:909 +#: dcim/tables/devices.py:913 msgid "Module Status" msgstr "État du module" -#: dcim/tables/devices.py:951 dcim/tables/devicetypes.py:308 +#: dcim/tables/devices.py:955 dcim/tables/devicetypes.py:308 #: templates/dcim/inventoryitem.html:41 msgid "Component" msgstr "Composant" -#: dcim/tables/devices.py:1006 +#: dcim/tables/devices.py:1010 msgid "Items" msgstr "Objets" @@ -6249,7 +6245,7 @@ msgid "Cluster type (slug)" msgstr "Type de cluster (slug)" #: extras/filtersets.py:490 ipam/forms/bulk_edit.py:475 -#: ipam/forms/model_forms.py:585 virtualization/forms/filtersets.py:108 +#: ipam/forms/model_forms.py:589 virtualization/forms/filtersets.py:108 msgid "Cluster group" msgstr "Groupe de clusters" @@ -6380,8 +6376,8 @@ msgid "Is active" msgstr "Est actif" #: extras/forms/bulk_import.py:34 extras/forms/bulk_import.py:115 -#: extras/forms/bulk_import.py:130 extras/forms/bulk_import.py:153 -#: extras/forms/bulk_import.py:177 extras/forms/filtersets.py:114 +#: extras/forms/bulk_import.py:136 extras/forms/bulk_import.py:159 +#: extras/forms/bulk_import.py:183 extras/forms/filtersets.py:114 #: extras/forms/filtersets.py:160 extras/forms/filtersets.py:201 #: extras/forms/model_forms.py:43 extras/forms/model_forms.py:127 #: extras/forms/model_forms.py:156 extras/forms/model_forms.py:197 @@ -6390,8 +6386,8 @@ msgid "Content types" msgstr "Types de contenu" #: extras/forms/bulk_import.py:36 extras/forms/bulk_import.py:117 -#: extras/forms/bulk_import.py:132 extras/forms/bulk_import.py:155 -#: extras/forms/bulk_import.py:179 tenancy/forms/bulk_import.py:96 +#: extras/forms/bulk_import.py:138 extras/forms/bulk_import.py:161 +#: extras/forms/bulk_import.py:185 tenancy/forms/bulk_import.py:96 msgid "One or more assigned object types" msgstr "Un ou plusieurs types d'objets attribués" @@ -6438,29 +6434,39 @@ msgstr "" "virgules avec des libellés facultatifs séparés par deux points : " "« Choice1:First Choice, Choice2:Second Choice »" -#: extras/forms/bulk_import.py:182 +#: extras/forms/bulk_import.py:120 extras/models/models.py:353 +msgid "button class" +msgstr "classe de boutons" + +#: extras/forms/bulk_import.py:123 extras/models/models.py:357 +msgid "" +"The class of the first link in a group will be used for the dropdown button" +msgstr "" +"La classe du premier lien d'un groupe sera utilisée pour le bouton déroulant" + +#: extras/forms/bulk_import.py:188 msgid "Action object" msgstr "Objet d'action" -#: extras/forms/bulk_import.py:184 +#: extras/forms/bulk_import.py:190 msgid "Webhook name or script as dotted path module.Class" msgstr "Nom du webhook ou script sous forme de chemin pointillé module.Class" -#: extras/forms/bulk_import.py:205 +#: extras/forms/bulk_import.py:211 #, python-brace-format msgid "Webhook {name} not found" msgstr "Webhook {name} introuvable" -#: extras/forms/bulk_import.py:214 +#: extras/forms/bulk_import.py:220 #, python-brace-format msgid "Script {name} not found" msgstr "Scénario {name} introuvable" -#: extras/forms/bulk_import.py:236 +#: extras/forms/bulk_import.py:242 msgid "Assigned object type" msgstr "Type d'objet attribué" -#: extras/forms/bulk_import.py:241 +#: extras/forms/bulk_import.py:247 msgid "The classification of entry" msgstr "La classification de l'entrée" @@ -7432,16 +7438,6 @@ msgstr "Code modèle Jinja2 pour l'URL du lien" msgid "Links with the same group will appear as a dropdown menu" msgstr "Les liens avec le même groupe apparaîtront dans un menu déroulant" -#: extras/models/models.py:353 -msgid "button class" -msgstr "classe de boutons" - -#: extras/models/models.py:357 -msgid "" -"The class of the first link in a group will be used for the dropdown button" -msgstr "" -"La classe du premier lien d'un groupe sera utilisée pour le bouton déroulant" - #: extras/models/models.py:360 msgid "new window" msgstr "nouvelle fenêtre" @@ -7634,7 +7630,7 @@ msgid "staged changes" msgstr "modifications échelonnées" #: extras/models/tags.py:40 -msgid "The object type(s) to which this this tag can be applied." +msgid "The object type(s) to which this tag can be applied." msgstr "Le ou les types d'objets auxquels cette balise peut être appliquée." #: extras/models/tags.py:49 @@ -7935,7 +7931,7 @@ msgid "VLAN number (1-4094)" msgstr "Numéro de VLAN (1-4094)" #: ipam/filtersets.py:437 ipam/filtersets.py:441 ipam/filtersets.py:533 -#: ipam/forms/model_forms.py:444 templates/tenancy/contact.html:54 +#: ipam/forms/model_forms.py:430 templates/tenancy/contact.html:54 #: tenancy/forms/bulk_edit.py:112 msgid "Address" msgstr "Adresse" @@ -8104,7 +8100,7 @@ msgid "Authentication key" msgstr "Clé d'authentification" #: ipam/forms/bulk_edit.py:404 ipam/forms/filtersets.py:369 -#: ipam/forms/model_forms.py:455 netbox/navigation/menu.py:376 +#: ipam/forms/model_forms.py:441 netbox/navigation/menu.py:376 #: templates/ipam/fhrpgroup.html:51 #: templates/wireless/inc/authentication_attrs.html:5 #: wireless/forms/bulk_edit.py:90 wireless/forms/bulk_edit.py:137 @@ -8121,11 +8117,11 @@ msgstr "VID VLAN minimum pour enfants" msgid "Maximum child VLAN VID" msgstr "VID VLAN maximum pour enfants" -#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:527 +#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:531 msgid "Scope type" msgstr "Type de portée" -#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:600 +#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:604 #: ipam/tables/vlans.py:71 templates/ipam/vlangroup.html:39 msgid "Scope" msgstr "Champ" @@ -8134,8 +8130,8 @@ msgstr "Champ" msgid "Site & Group" msgstr "Site et groupe" -#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:663 -#: ipam/forms/model_forms.py:697 ipam/tables/services.py:19 +#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:667 +#: ipam/forms/model_forms.py:701 ipam/tables/services.py:19 #: ipam/tables/services.py:49 templates/ipam/service.html:39 #: templates/ipam/servicetemplate.html:24 msgid "Ports" @@ -8175,7 +8171,7 @@ msgid "Parent device of assigned interface (if any)" msgstr "Appareil parent auquel est attribuée l'interface (le cas échéant)" #: ipam/forms/bulk_import.py:310 ipam/forms/bulk_import.py:496 -#: ipam/forms/model_forms.py:691 virtualization/filtersets.py:284 +#: ipam/forms/model_forms.py:695 virtualization/filtersets.py:284 #: virtualization/filtersets.py:323 virtualization/forms/bulk_edit.py:199 #: virtualization/forms/bulk_edit.py:325 #: virtualization/forms/bulk_import.py:146 @@ -8354,8 +8350,8 @@ msgstr "Port" #: virtualization/forms/filtersets.py:189 #: virtualization/forms/filtersets.py:234 #: virtualization/forms/model_forms.py:223 -#: virtualization/tables/virtualmachines.py:115 -#: virtualization/tables/virtualmachines.py:168 vpn/choices.py:45 +#: virtualization/tables/virtualmachines.py:128 +#: virtualization/tables/virtualmachines.py:181 vpn/choices.py:45 #: vpn/forms/filtersets.py:289 vpn/forms/model_forms.py:161 #: vpn/forms/model_forms.py:172 vpn/forms/model_forms.py:274 msgid "Virtual Machine" @@ -8378,7 +8374,7 @@ msgstr "Affectation de site/VLAN" msgid "IP Range" msgstr "Plage IP" -#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:454 +#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:440 #: templates/ipam/fhrpgroup.html:19 templates/ipam/ipaddress_edit.html:52 msgid "FHRP Group" msgstr "Groupe FHRP" @@ -8392,7 +8388,7 @@ msgstr "" msgid "An IP address can only be assigned to a single object." msgstr "Une adresse IP ne peut être attribuée qu'à un seul objet." -#: ipam/forms/model_forms.py:357 ipam/models/ip.py:877 +#: ipam/forms/model_forms.py:357 ipam/models/ip.py:896 msgid "" "Cannot reassign IP address while it is designated as the primary IP for the " "parent object" @@ -8407,36 +8403,25 @@ msgstr "" "Seules les adresses IP attribuées à une interface peuvent être désignées " "comme adresses IP principales." -#: ipam/forms/model_forms.py:373 -#, python-brace-format -msgid "{ip} is a network ID, which may not be assigned to an interface." -msgstr "" -"{ip} est un identifiant réseau, qui ne peut pas être attribué à une " -"interface." - -#: ipam/forms/model_forms.py:379 -#, python-brace-format -msgid "" -"{ip} is a broadcast address, which may not be assigned to an interface." -msgstr "" -"{ip} est une adresse de diffusion, qui ne peut pas être attribuée à une " -"interface." - -#: ipam/forms/model_forms.py:456 +#: ipam/forms/model_forms.py:442 msgid "Virtual IP Address" msgstr "Adresse IP virtuelle" -#: ipam/forms/model_forms.py:598 ipam/forms/model_forms.py:637 +#: ipam/forms/model_forms.py:523 +msgid "Assignment already exists" +msgstr "L'affectation existe déjà" + +#: ipam/forms/model_forms.py:602 ipam/forms/model_forms.py:641 #: ipam/tables/ip.py:250 templates/ipam/vlan_edit.html:37 #: templates/ipam/vlangroup.html:27 msgid "VLAN Group" msgstr "Groupe VLAN" -#: ipam/forms/model_forms.py:599 +#: ipam/forms/model_forms.py:603 msgid "Child VLANs" msgstr "VLAN pour enfants" -#: ipam/forms/model_forms.py:668 ipam/forms/model_forms.py:702 +#: ipam/forms/model_forms.py:672 ipam/forms/model_forms.py:706 msgid "" "Comma-separated list of one or more port numbers. A range may be specified " "using a hyphen." @@ -8444,15 +8429,15 @@ msgstr "" "Liste séparée par des virgules d'un ou de plusieurs numéros de port. Une " "plage peut être spécifiée à l'aide d'un trait d'union." -#: ipam/forms/model_forms.py:673 templates/ipam/servicetemplate.html:12 +#: ipam/forms/model_forms.py:677 templates/ipam/servicetemplate.html:12 msgid "Service Template" msgstr "Modèle de service" -#: ipam/forms/model_forms.py:724 +#: ipam/forms/model_forms.py:728 msgid "Service template" msgstr "Modèle de service" -#: ipam/forms/model_forms.py:754 +#: ipam/forms/model_forms.py:758 msgid "" "Must specify name, protocol, and port(s) if not using a service template." msgstr "" @@ -8621,12 +8606,12 @@ msgstr "préfixes" msgid "Cannot create prefix with /0 mask." msgstr "Impossible de créer un préfixe avec le masque /0." -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 #, python-brace-format msgid "VRF {vrf}" msgstr "VRF {vrf}" -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 msgid "global table" msgstr "tableau global" @@ -8725,12 +8710,27 @@ msgstr "Adresses IP" msgid "Cannot create IP address with /0 mask." msgstr "Impossible de créer une adresse IP avec le masque /0." -#: ipam/models/ip.py:856 +#: ipam/models/ip.py:850 +#, python-brace-format +msgid "{ip} is a network ID, which may not be assigned to an interface." +msgstr "" +"{ip} est un identifiant réseau, qui ne peut pas être attribué à une " +"interface." + +#: ipam/models/ip.py:861 +#, python-brace-format +msgid "" +"{ip} is a broadcast address, which may not be assigned to an interface." +msgstr "" +"{ip} est une adresse de diffusion, qui ne peut pas être attribuée à une " +"interface." + +#: ipam/models/ip.py:875 #, python-brace-format msgid "Duplicate IP address found in {table}: {ipaddress}" msgstr "Adresse IP dupliquée trouvée dans {table}: {ipaddress}" -#: ipam/models/ip.py:883 +#: ipam/models/ip.py:902 msgid "Only IPv6 addresses can be assigned SLAAC status" msgstr "Seules les adresses IPv6 peuvent se voir attribuer le statut SLAAC" @@ -9522,7 +9522,7 @@ msgstr "Machines virtuelles" #: templates/virtualization/virtualmachine.html:177 #: templates/virtualization/virtualmachine/base.html:32 #: templates/virtualization/virtualmachine_list.html:21 -#: virtualization/tables/virtualmachines.py:90 virtualization/views.py:389 +#: virtualization/tables/virtualmachines.py:103 virtualization/views.py:389 msgid "Virtual Disks" msgstr "Disques virtuels" @@ -10451,8 +10451,8 @@ msgstr "Planification" #: templates/core/job.html:66 #, python-format -msgid "every %(interval)s seconds" -msgstr "chaque %(interval)s secondes" +msgid "every %(interval)s minutes" +msgstr "chaque %(interval)s minutes" #: templates/dcim/bulk_disconnect.html:9 #, python-format @@ -13014,70 +13014,70 @@ msgstr "Les contraintes ne sont pas prises en charge pour ce type d'objet." msgid "Invalid filter for {model}: {error}" msgstr "Filtre non valide pour {model}: {error}" -#: users/models.py:54 +#: users/models.py:55 msgid "user" msgstr "utilisateur" -#: users/models.py:55 +#: users/models.py:56 msgid "users" msgstr "utilisateurs" -#: users/models.py:66 +#: users/models.py:67 msgid "A user with this username already exists." msgstr "Un utilisateur avec ce nom d'utilisateur existe déjà." -#: users/models.py:78 vpn/models/crypto.py:42 +#: users/models.py:79 vpn/models/crypto.py:42 msgid "group" msgstr "groupe" -#: users/models.py:79 +#: users/models.py:80 msgid "groups" msgstr "groupes" -#: users/models.py:106 users/models.py:107 +#: users/models.py:107 users/models.py:108 msgid "user preferences" msgstr "préférences de l'utilisateur" -#: users/models.py:174 +#: users/models.py:175 #, python-brace-format msgid "Key '{path}' is a leaf node; cannot assign new keys" msgstr "" "Clé '{path}'est un nœud feuille ; impossible d'attribuer de nouvelles clés" -#: users/models.py:186 +#: users/models.py:187 #, python-brace-format msgid "Key '{path}' is a dictionary; cannot assign a non-dictionary value" msgstr "" "Clé '{path}'est un dictionnaire ; impossible d'attribuer une valeur autre " "que celle du dictionnaire" -#: users/models.py:252 +#: users/models.py:253 msgid "expires" msgstr "expire" -#: users/models.py:257 +#: users/models.py:258 msgid "last used" msgstr "utilisé pour la dernière fois" -#: users/models.py:262 +#: users/models.py:263 msgid "key" msgstr "clé" -#: users/models.py:268 +#: users/models.py:269 msgid "write enabled" msgstr "écriture activée" -#: users/models.py:270 +#: users/models.py:271 msgid "Permit create/update/delete operations using this key" msgstr "" "Autoriser les opérations de création/mise à jour/suppression à l'aide de " "cette clé" -#: users/models.py:281 +#: users/models.py:282 msgid "allowed IPs" msgstr "adresses IP autorisées" -#: users/models.py:283 +#: users/models.py:284 msgid "" "Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for" " no restrictions. Ex: \"10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64\"" @@ -13086,34 +13086,34 @@ msgstr "" "Laissez ce champ vide pour éviter toute restriction. Par exemple : " "« 10.1.1.0/24, 192.168.10.16/32, 2001 : DB 8:1 : /64 »" -#: users/models.py:291 +#: users/models.py:296 msgid "token" msgstr "jeton" -#: users/models.py:292 +#: users/models.py:297 msgid "tokens" msgstr "jetons" -#: users/models.py:373 +#: users/models.py:378 msgid "The list of actions granted by this permission" msgstr "La liste des actions accordées par cette autorisation" -#: users/models.py:378 +#: users/models.py:383 msgid "constraints" msgstr "entraves" -#: users/models.py:379 +#: users/models.py:384 msgid "" "Queryset filter matching the applicable objects of the selected type(s)" msgstr "" "Filtre Queryset correspondant aux objets applicables du ou des types " "sélectionnés" -#: users/models.py:386 +#: users/models.py:391 msgid "permission" msgstr "autorisation" -#: users/models.py:387 +#: users/models.py:392 msgid "permissions" msgstr "autorisations" @@ -13392,47 +13392,56 @@ msgstr "" "Cet objet a été modifié depuis le rendu du formulaire. Consultez le journal " "des modifications de l'objet pour plus de détails." -#: utilities/forms/utils.py:42 utilities/forms/utils.py:65 -#: utilities/forms/utils.py:77 utilities/forms/utils.py:80 +#: utilities/forms/utils.py:42 utilities/forms/utils.py:68 +#: utilities/forms/utils.py:85 utilities/forms/utils.py:87 #, python-brace-format msgid "Range \"{value}\" is invalid." msgstr "Gamme »{value}« n'est pas valide." -#: utilities/forms/utils.py:225 +#: utilities/forms/utils.py:74 +#, python-brace-format +msgid "" +"Invalid range: Ending value ({end}) must be greater than beginning value " +"({begin})." +msgstr "" +"Plage non valide : valeur de fin ({end}) doit être supérieur à la valeur de " +"départ ({begin})." + +#: utilities/forms/utils.py:232 #, python-brace-format msgid "Duplicate or conflicting column header for \"{field}\"" msgstr "En-tête de colonne dupliqué ou en conflit pour »{field}«" -#: utilities/forms/utils.py:231 +#: utilities/forms/utils.py:238 #, python-brace-format msgid "Duplicate or conflicting column header for \"{header}\"" msgstr "En-tête de colonne dupliqué ou en conflit pour »{header}«" -#: utilities/forms/utils.py:240 +#: utilities/forms/utils.py:247 #, python-brace-format msgid "Row {row}: Expected {count_expected} columns but found {count_found}" msgstr "" "Rangée {row}: Prévu {count_expected} colonnes mais trouvées {count_found}" -#: utilities/forms/utils.py:263 +#: utilities/forms/utils.py:270 #, python-brace-format msgid "Unexpected column header \"{field}\" found." msgstr "En-tête de colonne inattendu »{field}« trouvé." -#: utilities/forms/utils.py:265 +#: utilities/forms/utils.py:272 #, python-brace-format msgid "Column \"{field}\" is not a related object; cannot use dots" msgstr "" "Colonne »{field}« n'est pas un objet apparenté ; ne peut pas utiliser de " "points" -#: utilities/forms/utils.py:269 +#: utilities/forms/utils.py:276 #, python-brace-format msgid "Invalid related object attribute for column \"{field}\": {to_field}" msgstr "" "Attribut d'objet associé non valide pour la colonne »{field}« : {to_field}" -#: utilities/forms/utils.py:277 +#: utilities/forms/utils.py:284 #, python-brace-format msgid "Required column header \"{header}\" not found." msgstr "En-tête de colonne obligatoire »{header}« introuvable." @@ -14073,6 +14082,11 @@ msgstr "Proposition" msgid "Assigned Object Type" msgstr "Type d'objet attribué" +#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 +#: vpn/forms/model_forms.py:241 vpn/tables/tunnels.py:91 +msgid "Tunnel interface" +msgstr "Interface de tunnel" + #: vpn/forms/model_forms.py:147 msgid "First Termination" msgstr "Première résiliation" diff --git a/netbox/translations/ja/LC_MESSAGES/django.mo b/netbox/translations/ja/LC_MESSAGES/django.mo index c190f234c9e29fce5fe4612e92ab9ca67ebe8213..2962616319d987b8cfaa1e2dd83024464a28c60c 100644 GIT binary patch delta 63825 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 delta 64316 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 JxP4EO{{i~-WKjSB diff --git a/netbox/translations/ja/LC_MESSAGES/django.po b/netbox/translations/ja/LC_MESSAGES/django.po index 14ecb3cb8..5b79cf0dd 100644 --- a/netbox/translations/ja/LC_MESSAGES/django.po +++ b/netbox/translations/ja/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-21 19:48+0000\n" +"POT-Creation-Date: 2024-04-04 19:11+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2024\n" "Language-Team: Japanese (https://app.transifex.com/netbox-community/teams/178115/ja/)\n" @@ -65,8 +65,8 @@ msgid "Your preferences have been updated." msgstr "設定が更新されました。" #: circuits/choices.py:21 dcim/choices.py:20 dcim/choices.py:102 -#: dcim/choices.py:174 dcim/choices.py:220 dcim/choices.py:1419 -#: dcim/choices.py:1495 dcim/choices.py:1545 virtualization/choices.py:20 +#: dcim/choices.py:174 dcim/choices.py:220 dcim/choices.py:1425 +#: dcim/choices.py:1501 dcim/choices.py:1551 virtualization/choices.py:20 #: virtualization/choices.py:45 vpn/choices.py:18 msgid "Planned" msgstr "計画中" @@ -76,8 +76,8 @@ msgid "Provisioning" msgstr "開通" #: circuits/choices.py:23 dcim/choices.py:22 dcim/choices.py:103 -#: dcim/choices.py:173 dcim/choices.py:219 dcim/choices.py:1494 -#: dcim/choices.py:1544 extras/tables/tables.py:380 ipam/choices.py:31 +#: dcim/choices.py:173 dcim/choices.py:219 dcim/choices.py:1500 +#: dcim/choices.py:1550 extras/tables/tables.py:380 ipam/choices.py:31 #: ipam/choices.py:49 ipam/choices.py:69 ipam/choices.py:154 #: templates/extras/configcontext.html:26 templates/users/user.html:38 #: users/forms/bulk_edit.py:36 virtualization/choices.py:22 @@ -86,7 +86,7 @@ msgid "Active" msgstr "アクティブ" #: circuits/choices.py:24 dcim/choices.py:172 dcim/choices.py:218 -#: dcim/choices.py:1493 dcim/choices.py:1546 virtualization/choices.py:24 +#: dcim/choices.py:1499 dcim/choices.py:1552 virtualization/choices.py:24 #: virtualization/choices.py:43 msgid "Offline" msgstr "オフライン" @@ -99,7 +99,7 @@ msgstr "解約" msgid "Decommissioned" msgstr "廃止" -#: circuits/filtersets.py:29 circuits/filtersets.py:184 dcim/filtersets.py:124 +#: circuits/filtersets.py:29 circuits/filtersets.py:190 dcim/filtersets.py:124 #: dcim/filtersets.py:185 dcim/filtersets.py:260 dcim/filtersets.py:369 #: dcim/filtersets.py:903 dcim/filtersets.py:1207 dcim/filtersets.py:1702 #: dcim/filtersets.py:1945 dcim/filtersets.py:2003 ipam/filtersets.py:305 @@ -108,7 +108,7 @@ msgstr "廃止" msgid "Region (ID)" msgstr "リージョン (ID)" -#: circuits/filtersets.py:36 circuits/filtersets.py:191 dcim/filtersets.py:130 +#: circuits/filtersets.py:36 circuits/filtersets.py:197 dcim/filtersets.py:130 #: dcim/filtersets.py:192 dcim/filtersets.py:267 dcim/filtersets.py:376 #: dcim/filtersets.py:910 dcim/filtersets.py:1214 dcim/filtersets.py:1709 #: dcim/filtersets.py:1952 dcim/filtersets.py:2010 extras/filtersets.py:414 @@ -118,7 +118,7 @@ msgstr "リージョン (ID)" msgid "Region (slug)" msgstr "リージョン (slug)" -#: circuits/filtersets.py:42 circuits/filtersets.py:197 dcim/filtersets.py:198 +#: circuits/filtersets.py:42 circuits/filtersets.py:203 dcim/filtersets.py:198 #: dcim/filtersets.py:273 dcim/filtersets.py:382 dcim/filtersets.py:916 #: dcim/filtersets.py:1220 dcim/filtersets.py:1715 dcim/filtersets.py:1958 #: dcim/filtersets.py:2016 ipam/filtersets.py:318 ipam/filtersets.py:909 @@ -126,7 +126,7 @@ msgstr "リージョン (slug)" msgid "Site group (ID)" msgstr "サイトグループ (ID)" -#: circuits/filtersets.py:49 circuits/filtersets.py:204 dcim/filtersets.py:205 +#: circuits/filtersets.py:49 circuits/filtersets.py:210 dcim/filtersets.py:205 #: dcim/filtersets.py:280 dcim/filtersets.py:389 dcim/filtersets.py:923 #: dcim/filtersets.py:1227 dcim/filtersets.py:1722 dcim/filtersets.py:1965 #: dcim/filtersets.py:2023 extras/filtersets.py:420 ipam/filtersets.py:325 @@ -136,7 +136,7 @@ msgid "Site group (slug)" msgstr "サイトグループ (slug)" #: circuits/filtersets.py:54 circuits/forms/bulk_import.py:117 -#: circuits/forms/filtersets.py:47 circuits/forms/filtersets.py:171 +#: circuits/forms/filtersets.py:47 circuits/forms/filtersets.py:167 #: circuits/forms/model_forms.py:137 dcim/forms/bulk_edit.py:166 #: dcim/forms/bulk_edit.py:238 dcim/forms/bulk_edit.py:570 #: dcim/forms/bulk_edit.py:763 dcim/forms/bulk_import.py:130 @@ -159,8 +159,8 @@ msgstr "サイトグループ (slug)" #: ipam/forms/bulk_import.py:170 ipam/forms/bulk_import.py:437 #: ipam/forms/filtersets.py:152 ipam/forms/filtersets.py:226 #: ipam/forms/filtersets.py:417 ipam/forms/filtersets.py:470 -#: ipam/forms/model_forms.py:206 ipam/forms/model_forms.py:548 -#: ipam/forms/model_forms.py:640 ipam/tables/ip.py:244 +#: ipam/forms/model_forms.py:206 ipam/forms/model_forms.py:552 +#: ipam/forms/model_forms.py:644 ipam/tables/ip.py:244 #: ipam/tables/vlans.py:114 ipam/tables/vlans.py:216 #: templates/circuits/circuittermination_edit.html:20 #: templates/circuits/inc/circuit_termination.html:33 @@ -182,13 +182,13 @@ msgstr "サイトグループ (slug)" #: virtualization/forms/model_forms.py:107 #: virtualization/forms/model_forms.py:174 #: virtualization/tables/clusters.py:77 -#: virtualization/tables/virtualmachines.py:53 vpn/forms/filtersets.py:262 +#: virtualization/tables/virtualmachines.py:62 vpn/forms/filtersets.py:262 #: wireless/forms/model_forms.py:77 wireless/forms/model_forms.py:117 msgid "Site" msgstr "サイト" -#: circuits/filtersets.py:60 circuits/filtersets.py:215 -#: circuits/filtersets.py:252 dcim/filtersets.py:215 dcim/filtersets.py:290 +#: circuits/filtersets.py:60 circuits/filtersets.py:221 +#: circuits/filtersets.py:258 dcim/filtersets.py:215 dcim/filtersets.py:290 #: dcim/filtersets.py:363 extras/filtersets.py:436 ipam/filtersets.py:215 #: ipam/filtersets.py:335 ipam/filtersets.py:926 #: virtualization/filtersets.py:75 virtualization/filtersets.py:203 @@ -200,33 +200,39 @@ msgstr "サイト (slug)" msgid "ASN (ID)" msgstr "ASN (ID)" -#: circuits/filtersets.py:87 circuits/filtersets.py:114 -#: circuits/filtersets.py:148 -msgid "Provider (ID)" -msgstr "プロバイダ (ID)" +#: circuits/filtersets.py:71 circuits/forms/filtersets.py:27 +#: ipam/forms/model_forms.py:158 ipam/models/asns.py:108 +#: ipam/models/asns.py:125 ipam/tables/asn.py:41 templates/ipam/asn.html:20 +msgid "ASN" +msgstr "ASN" #: circuits/filtersets.py:93 circuits/filtersets.py:120 #: circuits/filtersets.py:154 +msgid "Provider (ID)" +msgstr "プロバイダ (ID)" + +#: circuits/filtersets.py:99 circuits/filtersets.py:126 +#: circuits/filtersets.py:160 msgid "Provider (slug)" msgstr "プロバイダ (slug)" -#: circuits/filtersets.py:159 +#: circuits/filtersets.py:165 msgid "Provider account (ID)" msgstr "プロバイダアカウント (ID)" -#: circuits/filtersets.py:164 +#: circuits/filtersets.py:170 msgid "Provider network (ID)" msgstr "プロバイダネットワーク (ID)" -#: circuits/filtersets.py:168 +#: circuits/filtersets.py:174 msgid "Circuit type (ID)" msgstr "回線タイプ (ID)" -#: circuits/filtersets.py:174 +#: circuits/filtersets.py:180 msgid "Circuit type (slug)" msgstr "回線タイプ (slug)" -#: circuits/filtersets.py:209 circuits/filtersets.py:246 +#: circuits/filtersets.py:215 circuits/filtersets.py:252 #: dcim/filtersets.py:209 dcim/filtersets.py:284 dcim/filtersets.py:357 #: dcim/filtersets.py:927 dcim/filtersets.py:1232 dcim/filtersets.py:1727 #: dcim/filtersets.py:1969 dcim/filtersets.py:2028 ipam/filtersets.py:209 @@ -236,13 +242,13 @@ msgstr "回線タイプ (slug)" msgid "Site (ID)" msgstr "サイト (ID)" -#: circuits/filtersets.py:238 core/filtersets.py:73 core/filtersets.py:132 +#: circuits/filtersets.py:244 core/filtersets.py:73 core/filtersets.py:132 #: dcim/filtersets.py:640 dcim/filtersets.py:1201 dcim/filtersets.py:2076 #: extras/filtersets.py:40 extras/filtersets.py:69 extras/filtersets.py:101 #: extras/filtersets.py:140 extras/filtersets.py:168 extras/filtersets.py:195 #: extras/filtersets.py:226 extras/filtersets.py:295 extras/filtersets.py:343 #: extras/filtersets.py:403 extras/filtersets.py:562 extras/filtersets.py:604 -#: extras/filtersets.py:645 ipam/forms/model_forms.py:430 +#: extras/filtersets.py:645 ipam/forms/model_forms.py:416 #: netbox/filtersets.py:275 netbox/forms/__init__.py:23 #: netbox/forms/base.py:163 templates/htmx/object_selector.html:28 #: templates/inc/filter_list.html:53 templates/ipam/ipaddress_assign.html:32 @@ -252,7 +258,7 @@ msgstr "サイト (ID)" msgid "Search" msgstr "検索" -#: circuits/filtersets.py:242 circuits/forms/bulk_edit.py:167 +#: circuits/filtersets.py:248 circuits/forms/bulk_edit.py:167 #: circuits/forms/model_forms.py:110 circuits/forms/model_forms.py:132 #: dcim/forms/connections.py:66 templates/circuits/circuit.html:15 #: templates/dcim/inc/cable_termination.html:55 @@ -260,11 +266,11 @@ msgstr "検索" msgid "Circuit" msgstr "回線" -#: circuits/filtersets.py:256 +#: circuits/filtersets.py:262 msgid "ProviderNetwork (ID)" msgstr "プロバイダネットワーク (ID)" -#: circuits/forms/bulk_edit.py:25 circuits/forms/filtersets.py:56 +#: circuits/forms/bulk_edit.py:25 circuits/forms/filtersets.py:52 #: circuits/forms/model_forms.py:26 circuits/tables/providers.py:33 #: dcim/forms/bulk_edit.py:126 dcim/forms/filtersets.py:187 #: dcim/forms/model_forms.py:126 dcim/tables/sites.py:94 @@ -375,8 +381,8 @@ msgstr "説明" #: circuits/forms/bulk_edit.py:46 circuits/forms/bulk_edit.py:68 #: circuits/forms/bulk_edit.py:118 circuits/forms/bulk_import.py:35 #: circuits/forms/bulk_import.py:50 circuits/forms/bulk_import.py:76 -#: circuits/forms/filtersets.py:70 circuits/forms/filtersets.py:88 -#: circuits/forms/filtersets.py:116 circuits/forms/filtersets.py:131 +#: circuits/forms/filtersets.py:66 circuits/forms/filtersets.py:84 +#: circuits/forms/filtersets.py:112 circuits/forms/filtersets.py:127 #: circuits/forms/model_forms.py:32 circuits/forms/model_forms.py:44 #: circuits/forms/model_forms.py:58 circuits/forms/model_forms.py:92 #: circuits/tables/circuits.py:55 circuits/tables/providers.py:72 @@ -388,18 +394,18 @@ msgstr "説明" msgid "Provider" msgstr "プロバイダ" -#: circuits/forms/bulk_edit.py:75 circuits/forms/filtersets.py:91 +#: circuits/forms/bulk_edit.py:75 circuits/forms/filtersets.py:87 #: templates/circuits/providernetwork.html:31 msgid "Service ID" msgstr "サービス ID" -#: circuits/forms/bulk_edit.py:95 circuits/forms/filtersets.py:107 +#: circuits/forms/bulk_edit.py:95 circuits/forms/filtersets.py:103 #: dcim/forms/bulk_edit.py:204 dcim/forms/bulk_edit.py:500 #: dcim/forms/bulk_edit.py:694 dcim/forms/bulk_edit.py:1063 #: dcim/forms/bulk_edit.py:1090 dcim/forms/bulk_edit.py:1562 #: dcim/forms/filtersets.py:977 dcim/forms/filtersets.py:1353 -#: dcim/forms/filtersets.py:1374 dcim/tables/devices.py:722 -#: dcim/tables/devices.py:782 dcim/tables/devices.py:1009 +#: dcim/forms/filtersets.py:1374 dcim/tables/devices.py:726 +#: dcim/tables/devices.py:786 dcim/tables/devices.py:1013 #: dcim/tables/devicetypes.py:245 dcim/tables/devicetypes.py:260 #: dcim/tables/racks.py:32 extras/forms/bulk_edit.py:259 #: extras/tables/tables.py:328 templates/circuits/circuittype.html:33 @@ -411,7 +417,7 @@ msgid "Color" msgstr "色" #: circuits/forms/bulk_edit.py:113 circuits/forms/bulk_import.py:89 -#: circuits/forms/filtersets.py:126 core/forms/bulk_edit.py:17 +#: circuits/forms/filtersets.py:122 core/forms/bulk_edit.py:17 #: core/forms/filtersets.py:29 core/tables/data.py:20 core/tables/jobs.py:18 #: dcim/forms/bulk_edit.py:281 dcim/forms/bulk_edit.py:672 #: dcim/forms/bulk_edit.py:811 dcim/forms/bulk_edit.py:879 @@ -430,7 +436,7 @@ msgstr "色" #: dcim/forms/filtersets.py:1253 dcim/forms/filtersets.py:1348 #: dcim/forms/filtersets.py:1369 dcim/forms/object_import.py:89 #: dcim/forms/object_import.py:118 dcim/forms/object_import.py:150 -#: dcim/tables/devices.py:211 dcim/tables/devices.py:838 +#: dcim/tables/devices.py:211 dcim/tables/devices.py:842 #: dcim/tables/power.py:77 extras/forms/bulk_import.py:39 #: extras/tables/tables.py:278 extras/tables/tables.py:350 #: extras/tables/tables.py:448 netbox/tables/tables.py:234 @@ -455,12 +461,12 @@ msgid "Type" msgstr "タイプ" #: circuits/forms/bulk_edit.py:123 circuits/forms/bulk_import.py:82 -#: circuits/forms/filtersets.py:139 circuits/forms/model_forms.py:97 +#: circuits/forms/filtersets.py:135 circuits/forms/model_forms.py:97 msgid "Provider account" msgstr "プロバイダアカウント" #: circuits/forms/bulk_edit.py:131 circuits/forms/bulk_import.py:95 -#: circuits/forms/filtersets.py:150 core/forms/filtersets.py:34 +#: circuits/forms/filtersets.py:146 core/forms/filtersets.py:34 #: core/forms/filtersets.py:75 core/tables/data.py:23 core/tables/jobs.py:26 #: dcim/forms/bulk_edit.py:104 dcim/forms/bulk_edit.py:179 #: dcim/forms/bulk_edit.py:260 dcim/forms/bulk_edit.py:593 @@ -474,8 +480,8 @@ msgstr "プロバイダアカウント" #: dcim/forms/filtersets.py:281 dcim/forms/filtersets.py:726 #: dcim/forms/filtersets.py:835 dcim/forms/filtersets.py:871 #: dcim/forms/filtersets.py:972 dcim/forms/filtersets.py:1083 -#: dcim/tables/devices.py:173 dcim/tables/devices.py:841 -#: dcim/tables/devices.py:1069 dcim/tables/modules.py:69 +#: dcim/tables/devices.py:173 dcim/tables/devices.py:845 +#: dcim/tables/devices.py:1073 dcim/tables/modules.py:69 #: dcim/tables/power.py:74 dcim/tables/racks.py:66 dcim/tables/sites.py:82 #: dcim/tables/sites.py:133 ipam/forms/bulk_edit.py:240 #: ipam/forms/bulk_edit.py:289 ipam/forms/bulk_edit.py:337 @@ -483,7 +489,7 @@ msgstr "プロバイダアカウント" #: ipam/forms/bulk_import.py:256 ipam/forms/bulk_import.py:292 #: ipam/forms/bulk_import.py:458 ipam/forms/filtersets.py:205 #: ipam/forms/filtersets.py:270 ipam/forms/filtersets.py:341 -#: ipam/forms/filtersets.py:482 ipam/forms/model_forms.py:449 +#: ipam/forms/filtersets.py:482 ipam/forms/model_forms.py:435 #: ipam/tables/ip.py:236 ipam/tables/ip.py:309 ipam/tables/ip.py:359 #: ipam/tables/ip.py:421 ipam/tables/ip.py:448 ipam/tables/vlans.py:122 #: ipam/tables/vlans.py:227 templates/circuits/circuit.html:35 @@ -504,7 +510,7 @@ msgstr "プロバイダアカウント" #: virtualization/forms/bulk_import.py:80 #: virtualization/forms/filtersets.py:61 #: virtualization/forms/filtersets.py:156 virtualization/tables/clusters.py:74 -#: virtualization/tables/virtualmachines.py:50 vpn/forms/bulk_edit.py:38 +#: virtualization/tables/virtualmachines.py:59 vpn/forms/bulk_edit.py:38 #: vpn/forms/bulk_import.py:37 vpn/forms/filtersets.py:46 #: vpn/tables/tunnels.py:48 wireless/forms/bulk_edit.py:42 #: wireless/forms/bulk_edit.py:104 wireless/forms/bulk_import.py:43 @@ -515,7 +521,7 @@ msgid "Status" msgstr "ステータス" #: circuits/forms/bulk_edit.py:137 circuits/forms/bulk_import.py:100 -#: circuits/forms/filtersets.py:119 dcim/forms/bulk_edit.py:120 +#: circuits/forms/filtersets.py:115 dcim/forms/bulk_edit.py:120 #: dcim/forms/bulk_edit.py:185 dcim/forms/bulk_edit.py:255 #: dcim/forms/bulk_edit.py:366 dcim/forms/bulk_edit.py:583 #: dcim/forms/bulk_edit.py:684 dcim/forms/bulk_edit.py:1590 @@ -573,15 +579,15 @@ msgstr "ステータス" msgid "Tenant" msgstr "テナント" -#: circuits/forms/bulk_edit.py:142 circuits/forms/filtersets.py:174 +#: circuits/forms/bulk_edit.py:142 circuits/forms/filtersets.py:170 msgid "Install date" msgstr "開通日" -#: circuits/forms/bulk_edit.py:147 circuits/forms/filtersets.py:179 +#: circuits/forms/bulk_edit.py:147 circuits/forms/filtersets.py:175 msgid "Termination date" msgstr "終了日" -#: circuits/forms/bulk_edit.py:153 circuits/forms/filtersets.py:186 +#: circuits/forms/bulk_edit.py:153 circuits/forms/filtersets.py:182 msgid "Commit rate (Kbps)" msgstr "保証帯域 (Kbps)" @@ -614,7 +620,7 @@ msgstr "割当プロバイダ" #: circuits/forms/bulk_import.py:70 dcim/forms/bulk_import.py:178 #: dcim/forms/bulk_import.py:388 dcim/forms/bulk_import.py:1108 -#: dcim/forms/bulk_import.py:1187 extras/forms/bulk_import.py:229 +#: dcim/forms/bulk_import.py:1187 extras/forms/bulk_import.py:235 msgid "RGB color in hexadecimal. Example:" msgstr "16 進数の RGB カラーコード。例:" @@ -650,12 +656,12 @@ msgstr "運用状況" msgid "Assigned tenant" msgstr "割当テナント" -#: circuits/forms/bulk_import.py:123 circuits/forms/filtersets.py:147 +#: circuits/forms/bulk_import.py:123 circuits/forms/filtersets.py:143 #: circuits/forms/model_forms.py:143 msgid "Provider network" msgstr "プロバイダネットワーク" -#: circuits/forms/filtersets.py:26 circuits/forms/filtersets.py:118 +#: circuits/forms/filtersets.py:26 circuits/forms/filtersets.py:114 #: dcim/forms/bulk_edit.py:247 dcim/forms/bulk_edit.py:345 #: dcim/forms/bulk_edit.py:575 dcim/forms/bulk_edit.py:622 #: dcim/forms/bulk_edit.py:772 dcim/forms/bulk_import.py:189 @@ -679,7 +685,7 @@ msgstr "プロバイダネットワーク" #: extras/filtersets.py:441 extras/forms/filtersets.py:328 #: ipam/forms/bulk_edit.py:456 ipam/forms/filtersets.py:168 #: ipam/forms/filtersets.py:400 ipam/forms/filtersets.py:422 -#: ipam/forms/filtersets.py:448 ipam/forms/model_forms.py:560 +#: ipam/forms/filtersets.py:448 ipam/forms/model_forms.py:564 #: templates/dcim/device.html:26 templates/dcim/device_edit.html:30 #: templates/dcim/inc/cable_termination.html:12 #: templates/dcim/location.html:27 templates/dcim/powerpanel.html:27 @@ -689,13 +695,7 @@ msgstr "プロバイダネットワーク" msgid "Location" msgstr "ロケーション" -#: circuits/forms/filtersets.py:27 ipam/forms/model_forms.py:158 -#: ipam/models/asns.py:108 ipam/models/asns.py:125 ipam/tables/asn.py:41 -#: templates/ipam/asn.html:20 -msgid "ASN" -msgstr "ASN" - -#: circuits/forms/filtersets.py:28 circuits/forms/filtersets.py:120 +#: circuits/forms/filtersets.py:28 circuits/forms/filtersets.py:116 #: dcim/forms/filtersets.py:136 dcim/forms/filtersets.py:150 #: dcim/forms/filtersets.py:166 dcim/forms/filtersets.py:198 #: dcim/forms/filtersets.py:249 dcim/forms/filtersets.py:334 @@ -708,7 +708,7 @@ msgstr "ASN" msgid "Contacts" msgstr "連絡先" -#: circuits/forms/filtersets.py:33 circuits/forms/filtersets.py:157 +#: circuits/forms/filtersets.py:33 circuits/forms/filtersets.py:153 #: dcim/forms/bulk_edit.py:110 dcim/forms/bulk_edit.py:222 #: dcim/forms/bulk_edit.py:747 dcim/forms/bulk_import.py:92 #: dcim/forms/filtersets.py:70 dcim/forms/filtersets.py:177 @@ -723,7 +723,7 @@ msgstr "連絡先" #: ipam/forms/bulk_edit.py:205 ipam/forms/bulk_edit.py:437 #: ipam/forms/bulk_edit.py:509 ipam/forms/filtersets.py:212 #: ipam/forms/filtersets.py:407 ipam/forms/filtersets.py:456 -#: ipam/forms/model_forms.py:532 templates/dcim/device.html:18 +#: ipam/forms/model_forms.py:536 templates/dcim/device.html:18 #: templates/dcim/rack.html:19 templates/dcim/rackreservation.html:25 #: templates/dcim/region.html:26 templates/dcim/site.html:31 #: templates/ipam/prefix.html:50 templates/ipam/vlan.html:19 @@ -733,7 +733,7 @@ msgstr "連絡先" msgid "Region" msgstr "リージョン" -#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:162 +#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:158 #: dcim/forms/bulk_edit.py:230 dcim/forms/bulk_edit.py:755 #: dcim/forms/filtersets.py:75 dcim/forms/filtersets.py:182 #: dcim/forms/filtersets.py:208 dcim/forms/filtersets.py:269 @@ -743,19 +743,15 @@ msgstr "リージョン" #: extras/filtersets.py:425 ipam/forms/bulk_edit.py:210 #: ipam/forms/bulk_edit.py:444 ipam/forms/bulk_edit.py:514 #: ipam/forms/filtersets.py:217 ipam/forms/filtersets.py:412 -#: ipam/forms/filtersets.py:461 ipam/forms/model_forms.py:545 +#: ipam/forms/filtersets.py:461 ipam/forms/model_forms.py:549 #: virtualization/forms/bulk_edit.py:85 virtualization/forms/filtersets.py:68 #: virtualization/forms/filtersets.py:134 #: virtualization/forms/model_forms.py:101 msgid "Site group" msgstr "サイトグループ" -#: circuits/forms/filtersets.py:51 -msgid "ASN (legacy)" -msgstr "ASN (レガシー)" - -#: circuits/forms/filtersets.py:65 circuits/forms/filtersets.py:83 -#: circuits/forms/filtersets.py:102 circuits/forms/filtersets.py:117 +#: circuits/forms/filtersets.py:61 circuits/forms/filtersets.py:79 +#: circuits/forms/filtersets.py:98 circuits/forms/filtersets.py:113 #: core/forms/filtersets.py:63 dcim/forms/bulk_edit.py:718 #: dcim/forms/filtersets.py:164 dcim/forms/filtersets.py:196 #: dcim/forms/filtersets.py:825 dcim/forms/filtersets.py:920 @@ -781,7 +777,7 @@ msgstr "ASN (レガシー)" msgid "Attributes" msgstr "属性" -#: circuits/forms/filtersets.py:73 circuits/tables/circuits.py:60 +#: circuits/forms/filtersets.py:69 circuits/tables/circuits.py:60 #: circuits/tables/providers.py:66 templates/circuits/circuit.html:23 #: templates/circuits/provideraccount.html:25 msgid "Account" @@ -802,7 +798,7 @@ msgstr "回線タイプ" #: dcim/models/device_component_templates.py:491 #: dcim/models/device_component_templates.py:591 #: dcim/models/device_components.py:976 dcim/models/device_components.py:1050 -#: dcim/models/device_components.py:1166 dcim/models/devices.py:467 +#: dcim/models/device_components.py:1166 dcim/models/devices.py:469 #: dcim/models/racks.py:43 extras/models/tags.py:28 msgid "color" msgstr "色" @@ -824,8 +820,8 @@ msgid "Unique circuit ID" msgstr "一意な回線 ID" #: circuits/models/circuits.py:67 core/models/data.py:55 -#: core/models/jobs.py:85 dcim/models/cables.py:49 dcim/models/devices.py:641 -#: dcim/models/devices.py:1165 dcim/models/devices.py:1374 +#: core/models/jobs.py:85 dcim/models/cables.py:49 dcim/models/devices.py:643 +#: dcim/models/devices.py:1170 dcim/models/devices.py:1379 #: dcim/models/power.py:95 dcim/models/racks.py:97 dcim/models/sites.py:154 #: dcim/models/sites.py:266 ipam/models/ip.py:252 ipam/models/ip.py:521 #: ipam/models/ip.py:729 ipam/models/vlans.py:175 @@ -904,7 +900,7 @@ msgstr "パッチパネル ID とポート番号" #: extras/models/models.py:541 extras/models/staging.py:31 #: extras/models/tags.py:32 netbox/models/__init__.py:109 #: netbox/models/__init__.py:144 netbox/models/__init__.py:190 -#: users/models.py:273 users/models.py:348 +#: users/models.py:274 users/models.py:353 #: virtualization/models/virtualmachines.py:282 msgid "description" msgstr "説明" @@ -930,8 +926,8 @@ msgstr "回線終端をサイトとプロバイダーネットワークの両方 #: circuits/models/providers.py:22 circuits/models/providers.py:66 #: circuits/models/providers.py:104 core/models/data.py:42 #: core/models/jobs.py:46 dcim/models/device_component_templates.py:43 -#: dcim/models/device_components.py:54 dcim/models/devices.py:581 -#: dcim/models/devices.py:1305 dcim/models/devices.py:1370 +#: dcim/models/device_components.py:54 dcim/models/devices.py:583 +#: dcim/models/devices.py:1310 dcim/models/devices.py:1375 #: dcim/models/power.py:39 dcim/models/power.py:91 dcim/models/racks.py:62 #: dcim/models/sites.py:138 extras/models/configs.py:36 #: extras/models/configs.py:215 extras/models/customfields.py:89 @@ -944,7 +940,7 @@ msgstr "回線終端をサイトとプロバイダーネットワークの両方 #: ipam/models/vrfs.py:79 netbox/models/__init__.py:136 #: netbox/models/__init__.py:180 tenancy/models/contacts.py:64 #: tenancy/models/tenants.py:20 tenancy/models/tenants.py:45 -#: users/models.py:344 virtualization/models/clusters.py:57 +#: users/models.py:349 virtualization/models/clusters.py:57 #: virtualization/models/virtualmachines.py:70 #: virtualization/models/virtualmachines.py:272 vpn/models/crypto.py:24 #: vpn/models/crypto.py:71 vpn/models/crypto.py:131 vpn/models/crypto.py:183 @@ -1002,13 +998,13 @@ msgstr "プロバイダネットワーク" #: core/tables/data.py:16 core/tables/jobs.py:14 dcim/forms/filtersets.py:60 #: dcim/forms/object_create.py:42 dcim/tables/devices.py:88 #: dcim/tables/devices.py:125 dcim/tables/devices.py:167 -#: dcim/tables/devices.py:318 dcim/tables/devices.py:400 -#: dcim/tables/devices.py:444 dcim/tables/devices.py:496 -#: dcim/tables/devices.py:548 dcim/tables/devices.py:668 -#: dcim/tables/devices.py:749 dcim/tables/devices.py:799 -#: dcim/tables/devices.py:865 dcim/tables/devices.py:980 -#: dcim/tables/devices.py:1000 dcim/tables/devices.py:1029 -#: dcim/tables/devices.py:1059 dcim/tables/devicetypes.py:32 +#: dcim/tables/devices.py:322 dcim/tables/devices.py:404 +#: dcim/tables/devices.py:448 dcim/tables/devices.py:500 +#: dcim/tables/devices.py:552 dcim/tables/devices.py:672 +#: dcim/tables/devices.py:753 dcim/tables/devices.py:803 +#: dcim/tables/devices.py:869 dcim/tables/devices.py:984 +#: dcim/tables/devices.py:1004 dcim/tables/devices.py:1033 +#: dcim/tables/devices.py:1063 dcim/tables/devicetypes.py:32 #: dcim/tables/power.py:22 dcim/tables/power.py:62 dcim/tables/racks.py:23 #: dcim/tables/racks.py:53 dcim/tables/sites.py:24 dcim/tables/sites.py:51 #: dcim/tables/sites.py:78 dcim/tables/sites.py:125 @@ -1074,9 +1070,9 @@ msgstr "プロバイダネットワーク" #: virtualization/forms/object_create.py:23 #: virtualization/tables/clusters.py:17 virtualization/tables/clusters.py:39 #: virtualization/tables/clusters.py:62 -#: virtualization/tables/virtualmachines.py:45 -#: virtualization/tables/virtualmachines.py:119 -#: virtualization/tables/virtualmachines.py:172 vpn/tables/crypto.py:18 +#: virtualization/tables/virtualmachines.py:54 +#: virtualization/tables/virtualmachines.py:132 +#: virtualization/tables/virtualmachines.py:185 vpn/tables/crypto.py:18 #: vpn/tables/crypto.py:57 vpn/tables/crypto.py:93 vpn/tables/crypto.py:129 #: vpn/tables/crypto.py:158 vpn/tables/l2vpn.py:23 vpn/tables/tunnels.py:18 #: vpn/tables/tunnels.py:40 wireless/tables/wirelesslan.py:18 @@ -1111,7 +1107,7 @@ msgstr "保証帯域" #: circuits/tables/circuits.py:75 circuits/tables/providers.py:48 #: circuits/tables/providers.py:82 circuits/tables/providers.py:107 -#: dcim/tables/devices.py:1042 dcim/tables/devicetypes.py:92 +#: dcim/tables/devices.py:1046 dcim/tables/devicetypes.py:92 #: dcim/tables/modules.py:29 dcim/tables/modules.py:72 dcim/tables/power.py:39 #: dcim/tables/power.py:96 dcim/tables/racks.py:76 dcim/tables/racks.py:156 #: dcim/tables/sites.py:103 extras/forms/bulk_edit.py:320 @@ -1123,7 +1119,7 @@ msgstr "保証帯域" #: templates/inc/panels/comments.html:6 tenancy/tables/contacts.py:68 #: tenancy/tables/tenants.py:46 utilities/forms/fields/fields.py:29 #: virtualization/tables/clusters.py:91 -#: virtualization/tables/virtualmachines.py:68 vpn/tables/crypto.py:37 +#: virtualization/tables/virtualmachines.py:81 vpn/tables/crypto.py:37 #: vpn/tables/crypto.py:74 vpn/tables/crypto.py:109 vpn/tables/crypto.py:140 #: vpn/tables/crypto.py:173 vpn/tables/l2vpn.py:37 vpn/tables/tunnels.py:61 #: wireless/tables/wirelesslan.py:27 wireless/tables/wirelesslan.py:58 @@ -1160,7 +1156,7 @@ msgid "Completed" msgstr "完了" #: core/choices.py:22 core/choices.py:59 dcim/choices.py:176 -#: dcim/choices.py:222 dcim/choices.py:1496 extras/choices.py:212 +#: dcim/choices.py:222 dcim/choices.py:1502 extras/choices.py:212 #: virtualization/choices.py:47 msgid "Failed" msgstr "失敗" @@ -1242,7 +1238,7 @@ msgstr "データソース (名前)" #: core/forms/bulk_edit.py:24 core/forms/filtersets.py:39 #: core/tables/data.py:26 dcim/forms/bulk_edit.py:1012 #: dcim/forms/bulk_edit.py:1285 dcim/forms/filtersets.py:1270 -#: dcim/tables/devices.py:573 dcim/tables/devicetypes.py:221 +#: dcim/tables/devices.py:577 dcim/tables/devicetypes.py:221 #: extras/forms/bulk_edit.py:97 extras/forms/bulk_edit.py:161 #: extras/forms/bulk_edit.py:220 extras/forms/filtersets.py:119 #: extras/forms/filtersets.py:206 extras/forms/filtersets.py:267 @@ -1377,7 +1373,7 @@ msgstr "同期するファイルをアップロードするか、データファ msgid "Rack Elevations" msgstr "ラック図" -#: core/forms/model_forms.py:148 dcim/choices.py:1407 +#: core/forms/model_forms.py:148 dcim/choices.py:1413 #: dcim/forms/bulk_edit.py:859 dcim/forms/bulk_edit.py:1242 #: dcim/forms/bulk_edit.py:1260 dcim/tables/racks.py:89 #: netbox/navigation/menu.py:276 netbox/navigation/menu.py:280 @@ -1438,7 +1434,7 @@ msgstr " (デフォルト)" #: core/models/config.py:18 core/models/data.py:282 core/models/files.py:27 #: core/models/jobs.py:50 extras/models/models.py:760 -#: netbox/models/features.py:52 users/models.py:248 +#: netbox/models/features.py:52 users/models.py:249 msgid "created" msgstr "作成日時" @@ -1496,7 +1492,7 @@ msgstr "URL" #: core/models/data.py:62 dcim/models/device_component_templates.py:392 #: dcim/models/device_components.py:513 extras/models/models.py:88 -#: extras/models/models.py:331 extras/models/models.py:556 users/models.py:353 +#: extras/models/models.py:331 extras/models/models.py:556 users/models.py:358 msgid "enabled" msgstr "有効" @@ -1707,7 +1703,7 @@ msgid "Staging" msgstr "ステージング" #: dcim/choices.py:23 dcim/choices.py:178 dcim/choices.py:223 -#: dcim/choices.py:1420 virtualization/choices.py:23 +#: dcim/choices.py:1426 virtualization/choices.py:23 #: virtualization/choices.py:48 msgid "Decommissioning" msgstr "廃止" @@ -1767,7 +1763,7 @@ msgstr "廃止済" msgid "Millimeters" msgstr "ミリメートル" -#: dcim/choices.py:115 dcim/choices.py:1442 +#: dcim/choices.py:115 dcim/choices.py:1448 msgid "Inches" msgstr "インチ" @@ -1779,8 +1775,8 @@ msgstr "インチ" #: dcim/forms/filtersets.py:226 dcim/forms/model_forms.py:73 #: dcim/forms/model_forms.py:94 dcim/forms/model_forms.py:172 #: dcim/forms/model_forms.py:962 dcim/forms/model_forms.py:1303 -#: dcim/forms/object_import.py:181 dcim/tables/devices.py:676 -#: dcim/tables/devices.py:960 extras/tables/tables.py:181 +#: dcim/forms/object_import.py:181 dcim/tables/devices.py:680 +#: dcim/tables/devices.py:964 extras/tables/tables.py:181 #: ipam/tables/fhrp.py:59 ipam/tables/ip.py:374 ipam/tables/services.py:44 #: templates/dcim/interface.html:105 templates/dcim/interface.html:321 #: templates/dcim/location.html:44 templates/dcim/region.html:38 @@ -1793,7 +1789,7 @@ msgstr "インチ" #: tenancy/forms/bulk_import.py:58 tenancy/forms/model_forms.py:24 #: tenancy/forms/model_forms.py:69 virtualization/forms/bulk_edit.py:206 #: virtualization/forms/bulk_import.py:151 -#: virtualization/tables/virtualmachines.py:142 wireless/forms/bulk_edit.py:23 +#: virtualization/tables/virtualmachines.py:155 wireless/forms/bulk_edit.py:23 #: wireless/forms/bulk_import.py:21 wireless/forms/model_forms.py:20 msgid "Parent" msgstr "親" @@ -1842,7 +1838,7 @@ msgstr "右から左" msgid "Side to rear" msgstr "側面から背面" -#: dcim/choices.py:198 dcim/choices.py:1215 +#: dcim/choices.py:198 dcim/choices.py:1221 msgid "Passive" msgstr "パッシブ" @@ -1870,8 +1866,8 @@ msgstr "International/ITA" msgid "Proprietary" msgstr "独自規格" -#: dcim/choices.py:534 dcim/choices.py:764 dcim/choices.py:1131 -#: dcim/choices.py:1133 dcim/choices.py:1338 dcim/choices.py:1340 +#: dcim/choices.py:534 dcim/choices.py:764 dcim/choices.py:1137 +#: dcim/choices.py:1139 dcim/choices.py:1344 dcim/choices.py:1346 #: netbox/navigation/menu.py:188 msgid "Other" msgstr "その他" @@ -1884,177 +1880,177 @@ msgstr "ITA/International" msgid "Physical" msgstr "物理" -#: dcim/choices.py:795 dcim/choices.py:949 +#: dcim/choices.py:795 dcim/choices.py:952 msgid "Virtual" msgstr "仮想" -#: dcim/choices.py:796 dcim/choices.py:1019 dcim/forms/bulk_edit.py:1398 +#: dcim/choices.py:796 dcim/choices.py:1022 dcim/forms/bulk_edit.py:1398 #: dcim/forms/filtersets.py:1233 dcim/forms/model_forms.py:888 #: dcim/forms/model_forms.py:1197 netbox/navigation/menu.py:128 #: netbox/navigation/menu.py:132 templates/dcim/interface.html:217 msgid "Wireless" msgstr "無線" -#: dcim/choices.py:947 +#: dcim/choices.py:950 msgid "Virtual interfaces" msgstr "仮想インタフェース" -#: dcim/choices.py:950 dcim/forms/bulk_edit.py:1295 +#: dcim/choices.py:953 dcim/forms/bulk_edit.py:1295 #: dcim/forms/bulk_import.py:785 dcim/forms/model_forms.py:876 -#: dcim/tables/devices.py:680 templates/dcim/interface.html:109 +#: dcim/tables/devices.py:684 templates/dcim/interface.html:109 #: templates/virtualization/vminterface.html:46 #: virtualization/forms/bulk_edit.py:211 #: virtualization/forms/bulk_import.py:158 -#: virtualization/tables/virtualmachines.py:146 +#: virtualization/tables/virtualmachines.py:159 msgid "Bridge" msgstr "ブリッジ" -#: dcim/choices.py:951 +#: dcim/choices.py:954 msgid "Link Aggregation Group (LAG)" msgstr "リンクアグリゲーション (LAG)" -#: dcim/choices.py:955 +#: dcim/choices.py:958 msgid "Ethernet (fixed)" msgstr "イーサネット (固定)" -#: dcim/choices.py:969 +#: dcim/choices.py:972 msgid "Ethernet (modular)" msgstr "イーサネット (モジュール)" -#: dcim/choices.py:1005 +#: dcim/choices.py:1008 msgid "Ethernet (backplane)" msgstr "イーサネット (バックプレーン)" -#: dcim/choices.py:1033 +#: dcim/choices.py:1036 msgid "Cellular" msgstr "セルラー" -#: dcim/choices.py:1080 dcim/forms/filtersets.py:302 +#: dcim/choices.py:1086 dcim/forms/filtersets.py:302 #: dcim/forms/filtersets.py:736 dcim/forms/filtersets.py:876 #: dcim/forms/filtersets.py:1426 templates/dcim/inventoryitem.html:53 #: templates/dcim/virtualchassis_edit.html:55 msgid "Serial" msgstr "シリアル" -#: dcim/choices.py:1095 +#: dcim/choices.py:1101 msgid "Coaxial" msgstr "同軸" -#: dcim/choices.py:1112 +#: dcim/choices.py:1118 msgid "Stacking" msgstr "スタック" -#: dcim/choices.py:1162 +#: dcim/choices.py:1168 msgid "Half" msgstr "半二重" -#: dcim/choices.py:1163 +#: dcim/choices.py:1169 msgid "Full" msgstr "全二重" -#: dcim/choices.py:1164 netbox/preferences.py:29 wireless/choices.py:480 +#: dcim/choices.py:1170 netbox/preferences.py:29 wireless/choices.py:480 msgid "Auto" msgstr "自動" -#: dcim/choices.py:1175 +#: dcim/choices.py:1181 msgid "Access" msgstr "アクセス" -#: dcim/choices.py:1176 ipam/tables/vlans.py:168 ipam/tables/vlans.py:213 +#: dcim/choices.py:1182 ipam/tables/vlans.py:168 ipam/tables/vlans.py:213 #: templates/dcim/inc/interface_vlans_table.html:7 msgid "Tagged" msgstr "タグ付き" -#: dcim/choices.py:1177 +#: dcim/choices.py:1183 msgid "Tagged (All)" msgstr "タグ付き (全て)" -#: dcim/choices.py:1206 +#: dcim/choices.py:1212 msgid "IEEE Standard" msgstr "IEEE スタンダード" -#: dcim/choices.py:1217 +#: dcim/choices.py:1223 msgid "Passive 24V (2-pair)" msgstr "パッシブ 24V (2 ペア)" -#: dcim/choices.py:1218 +#: dcim/choices.py:1224 msgid "Passive 24V (4-pair)" msgstr "パッシブ 24V (4ペア)" -#: dcim/choices.py:1219 +#: dcim/choices.py:1225 msgid "Passive 48V (2-pair)" msgstr "パッシブ 48V (2 ペア)" -#: dcim/choices.py:1220 +#: dcim/choices.py:1226 msgid "Passive 48V (4-pair)" msgstr "パッシブ 48V (4ペア)" -#: dcim/choices.py:1282 dcim/choices.py:1378 +#: dcim/choices.py:1288 dcim/choices.py:1384 msgid "Copper" msgstr "カッパー" -#: dcim/choices.py:1305 +#: dcim/choices.py:1311 msgid "Fiber Optic" msgstr "光ファイバー" -#: dcim/choices.py:1394 +#: dcim/choices.py:1400 msgid "Fiber" msgstr "ファイバー" -#: dcim/choices.py:1418 dcim/forms/filtersets.py:1140 +#: dcim/choices.py:1424 dcim/forms/filtersets.py:1140 msgid "Connected" msgstr "接続済" -#: dcim/choices.py:1437 +#: dcim/choices.py:1443 msgid "Kilometers" msgstr "キロメートル" -#: dcim/choices.py:1438 templates/dcim/cable_trace.html:62 +#: dcim/choices.py:1444 templates/dcim/cable_trace.html:62 msgid "Meters" msgstr "メートル" -#: dcim/choices.py:1439 +#: dcim/choices.py:1445 msgid "Centimeters" msgstr "センチメートル" -#: dcim/choices.py:1440 +#: dcim/choices.py:1446 msgid "Miles" msgstr "マイル" -#: dcim/choices.py:1441 templates/dcim/cable_trace.html:63 +#: dcim/choices.py:1447 templates/dcim/cable_trace.html:63 msgid "Feet" msgstr "フィート" -#: dcim/choices.py:1457 templates/dcim/device.html:332 +#: dcim/choices.py:1463 templates/dcim/device.html:332 #: templates/dcim/rack.html:157 msgid "Kilograms" msgstr "キログラム" -#: dcim/choices.py:1458 +#: dcim/choices.py:1464 msgid "Grams" msgstr "グラム" -#: dcim/choices.py:1459 templates/dcim/rack.html:158 +#: dcim/choices.py:1465 templates/dcim/rack.html:158 msgid "Pounds" msgstr "ポンド" -#: dcim/choices.py:1460 +#: dcim/choices.py:1466 msgid "Ounces" msgstr "オンス" -#: dcim/choices.py:1506 tenancy/choices.py:17 +#: dcim/choices.py:1512 tenancy/choices.py:17 msgid "Primary" msgstr "プライマリ" -#: dcim/choices.py:1507 +#: dcim/choices.py:1513 msgid "Redundant" msgstr "冗長" -#: dcim/choices.py:1528 +#: dcim/choices.py:1534 msgid "Single phase" msgstr "単相" -#: dcim/choices.py:1529 +#: dcim/choices.py:1535 msgid "Three-phase" msgstr "三相" @@ -2331,7 +2327,7 @@ msgid "Virtual Chassis (ID)" msgstr "バーチャルシャーシ (ID)" #: dcim/filtersets.py:1303 dcim/forms/filtersets.py:106 -#: dcim/tables/devices.py:235 netbox/navigation/menu.py:67 +#: dcim/tables/devices.py:239 netbox/navigation/menu.py:67 #: templates/dcim/device.html:123 templates/dcim/device_edit.html:93 #: templates/dcim/virtualchassis.html:20 #: templates/dcim/virtualchassis_add.html:8 @@ -2355,7 +2351,7 @@ msgstr "割当 VID" #: dcim/filtersets.py:1448 dcim/forms/bulk_edit.py:1374 #: dcim/forms/bulk_import.py:836 dcim/forms/filtersets.py:1328 #: dcim/forms/model_forms.py:1182 dcim/models/device_components.py:712 -#: dcim/tables/devices.py:642 ipam/filtersets.py:282 ipam/filtersets.py:293 +#: dcim/tables/devices.py:646 ipam/filtersets.py:282 ipam/filtersets.py:293 #: ipam/filtersets.py:449 ipam/filtersets.py:550 ipam/filtersets.py:561 #: ipam/forms/bulk_edit.py:226 ipam/forms/bulk_edit.py:281 #: ipam/forms/bulk_edit.py:323 ipam/forms/bulk_import.py:156 @@ -2363,8 +2359,8 @@ msgstr "割当 VID" #: ipam/forms/filtersets.py:66 ipam/forms/filtersets.py:167 #: ipam/forms/filtersets.py:295 ipam/forms/model_forms.py:59 #: ipam/forms/model_forms.py:203 ipam/forms/model_forms.py:246 -#: ipam/forms/model_forms.py:290 ipam/forms/model_forms.py:412 -#: ipam/forms/model_forms.py:426 ipam/forms/model_forms.py:440 +#: ipam/forms/model_forms.py:290 ipam/forms/model_forms.py:398 +#: ipam/forms/model_forms.py:412 ipam/forms/model_forms.py:426 #: ipam/models/ip.py:232 ipam/models/ip.py:511 ipam/models/ip.py:719 #: ipam/models/vrfs.py:62 ipam/tables/ip.py:241 ipam/tables/ip.py:306 #: ipam/tables/ip.py:356 ipam/tables/ip.py:445 @@ -2377,7 +2373,7 @@ msgstr "割当 VID" #: virtualization/forms/filtersets.py:220 #: virtualization/forms/model_forms.py:347 #: virtualization/models/virtualmachines.py:348 -#: virtualization/tables/virtualmachines.py:123 +#: virtualization/tables/virtualmachines.py:136 msgid "VRF" msgstr "VRF" @@ -2391,7 +2387,7 @@ msgid "L2VPN (ID)" msgstr "L2VPN (ID)" #: dcim/filtersets.py:1465 dcim/forms/filtersets.py:1333 -#: dcim/tables/devices.py:590 ipam/filtersets.py:973 +#: dcim/tables/devices.py:594 ipam/filtersets.py:973 #: ipam/forms/filtersets.py:499 ipam/tables/vlans.py:133 #: templates/dcim/interface.html:94 templates/ipam/vlan.html:69 #: templates/vpn/l2vpntermination.html:15 @@ -2462,7 +2458,7 @@ msgstr "タグ" #: dcim/forms/bulk_create.py:112 dcim/forms/filtersets.py:1390 #: dcim/forms/model_forms.py:426 dcim/forms/model_forms.py:475 #: dcim/forms/object_create.py:196 dcim/forms/object_create.py:352 -#: dcim/tables/devices.py:198 dcim/tables/devices.py:725 +#: dcim/tables/devices.py:198 dcim/tables/devices.py:729 #: dcim/tables/devicetypes.py:242 templates/dcim/device.html:45 #: templates/dcim/device.html:129 templates/dcim/modulebay.html:35 #: templates/dcim/virtualchassis.html:59 @@ -2479,7 +2475,7 @@ msgstr "英数字の範囲が使用できます。(作成する名前の数と #: dcim/forms/bulk_edit.py:115 dcim/forms/bulk_import.py:99 #: dcim/forms/model_forms.py:120 dcim/tables/sites.py:89 #: ipam/filtersets.py:936 ipam/forms/bulk_edit.py:528 -#: ipam/forms/bulk_import.py:444 ipam/forms/model_forms.py:509 +#: ipam/forms/bulk_import.py:444 ipam/forms/model_forms.py:495 #: ipam/tables/fhrp.py:67 ipam/tables/vlans.py:118 ipam/tables/vlans.py:221 #: templates/dcim/interface.html:294 templates/dcim/site.html:37 #: templates/ipam/inc/panels/fhrp_groups.html:10 templates/ipam/vlan.html:30 @@ -2529,8 +2525,8 @@ msgstr "タイムゾーン" #: dcim/forms/filtersets.py:704 dcim/forms/filtersets.py:1417 #: dcim/forms/model_forms.py:224 dcim/forms/model_forms.py:970 #: dcim/forms/model_forms.py:1311 dcim/forms/object_import.py:186 -#: dcim/tables/devices.py:202 dcim/tables/devices.py:833 -#: dcim/tables/devices.py:944 dcim/tables/devicetypes.py:300 +#: dcim/tables/devices.py:202 dcim/tables/devices.py:837 +#: dcim/tables/devices.py:948 dcim/tables/devicetypes.py:300 #: dcim/tables/racks.py:69 extras/filtersets.py:457 #: ipam/forms/bulk_edit.py:245 ipam/forms/bulk_edit.py:294 #: ipam/forms/bulk_edit.py:342 ipam/forms/bulk_edit.py:546 @@ -2539,7 +2535,7 @@ msgstr "タイムゾーン" #: ipam/forms/filtersets.py:232 ipam/forms/filtersets.py:278 #: ipam/forms/filtersets.py:346 ipam/forms/filtersets.py:490 #: ipam/forms/model_forms.py:187 ipam/forms/model_forms.py:222 -#: ipam/forms/model_forms.py:249 ipam/forms/model_forms.py:647 +#: ipam/forms/model_forms.py:249 ipam/forms/model_forms.py:651 #: ipam/tables/ip.py:257 ipam/tables/ip.py:313 ipam/tables/ip.py:363 #: ipam/tables/vlans.py:126 ipam/tables/vlans.py:230 #: templates/dcim/device.html:187 @@ -2557,7 +2553,7 @@ msgstr "タイムゾーン" #: virtualization/forms/bulk_import.py:106 #: virtualization/forms/filtersets.py:153 #: virtualization/forms/model_forms.py:198 -#: virtualization/tables/virtualmachines.py:65 vpn/forms/bulk_edit.py:86 +#: virtualization/tables/virtualmachines.py:74 vpn/forms/bulk_edit.py:86 #: vpn/forms/bulk_import.py:81 vpn/forms/filtersets.py:84 #: vpn/forms/model_forms.py:77 vpn/forms/model_forms.py:112 #: vpn/tables/tunnels.py:82 @@ -2651,7 +2647,7 @@ msgstr "重量単位" #: dcim/forms/model_forms.py:669 dcim/forms/object_create.py:399 #: dcim/tables/devices.py:194 dcim/tables/power.py:70 dcim/tables/racks.py:148 #: ipam/forms/bulk_edit.py:464 ipam/forms/filtersets.py:427 -#: ipam/forms/model_forms.py:571 templates/dcim/device.html:30 +#: ipam/forms/model_forms.py:575 templates/dcim/device.html:30 #: templates/dcim/inc/cable_termination.html:16 #: templates/dcim/powerfeed.html:31 templates/dcim/rack.html:14 #: templates/dcim/rack/base.html:4 templates/dcim/rack_edit.html:8 @@ -2684,7 +2680,7 @@ msgstr "ハードウェア" #: dcim/forms/model_forms.py:334 dcim/forms/model_forms.py:374 #: dcim/forms/model_forms.py:975 dcim/forms/model_forms.py:1316 #: dcim/forms/object_import.py:192 dcim/tables/devices.py:129 -#: dcim/tables/devices.py:205 dcim/tables/devices.py:947 +#: dcim/tables/devices.py:205 dcim/tables/devices.py:951 #: dcim/tables/devicetypes.py:81 dcim/tables/devicetypes.py:304 #: dcim/tables/modules.py:20 dcim/tables/modules.py:60 #: templates/dcim/devicetype.html:17 templates/dcim/inventoryitem.html:45 @@ -2731,7 +2727,7 @@ msgstr "デバイスタイプ" msgid "Module Type" msgstr "モジュールタイプ" -#: dcim/forms/bulk_edit.py:506 dcim/models/devices.py:472 +#: dcim/forms/bulk_edit.py:506 dcim/models/devices.py:474 msgid "VM role" msgstr "仮想マシンのロール" @@ -2763,13 +2759,15 @@ msgstr "デバイスロール" #: dcim/forms/bulk_edit.py:588 dcim/forms/bulk_import.py:443 #: dcim/forms/filtersets.py:723 dcim/forms/model_forms.py:389 -#: dcim/forms/model_forms.py:448 extras/filtersets.py:468 -#: templates/dcim/device.html:191 templates/dcim/platform.html:27 +#: dcim/forms/model_forms.py:448 dcim/tables/devices.py:215 +#: extras/filtersets.py:468 templates/dcim/device.html:191 +#: templates/dcim/platform.html:27 #: templates/virtualization/virtualmachine.html:30 #: virtualization/forms/bulk_edit.py:159 #: virtualization/forms/bulk_import.py:122 #: virtualization/forms/filtersets.py:164 #: virtualization/forms/model_forms.py:206 +#: virtualization/tables/virtualmachines.py:78 msgid "Platform" msgstr "プラットフォーム" @@ -2793,16 +2791,16 @@ msgstr "プラットフォーム" #: dcim/forms/model_forms.py:760 dcim/forms/model_forms.py:1011 #: dcim/forms/model_forms.py:1460 dcim/forms/object_create.py:256 #: dcim/tables/connections.py:22 dcim/tables/connections.py:41 -#: dcim/tables/connections.py:60 dcim/tables/devices.py:314 -#: dcim/tables/devices.py:379 dcim/tables/devices.py:423 -#: dcim/tables/devices.py:468 dcim/tables/devices.py:522 -#: dcim/tables/devices.py:614 dcim/tables/devices.py:715 -#: dcim/tables/devices.py:775 dcim/tables/devices.py:825 -#: dcim/tables/devices.py:885 dcim/tables/devices.py:937 -#: dcim/tables/devices.py:1063 dcim/tables/modules.py:52 +#: dcim/tables/connections.py:60 dcim/tables/devices.py:318 +#: dcim/tables/devices.py:383 dcim/tables/devices.py:427 +#: dcim/tables/devices.py:472 dcim/tables/devices.py:526 +#: dcim/tables/devices.py:618 dcim/tables/devices.py:719 +#: dcim/tables/devices.py:779 dcim/tables/devices.py:829 +#: dcim/tables/devices.py:889 dcim/tables/devices.py:941 +#: dcim/tables/devices.py:1067 dcim/tables/modules.py:52 #: extras/forms/filtersets.py:329 ipam/forms/bulk_import.py:303 #: ipam/forms/bulk_import.py:489 ipam/forms/filtersets.py:532 -#: ipam/forms/model_forms.py:685 ipam/tables/vlans.py:176 +#: ipam/forms/model_forms.py:689 ipam/tables/vlans.py:176 #: templates/dcim/consoleport.html:23 templates/dcim/consoleserverport.html:23 #: templates/dcim/device.html:14 templates/dcim/device.html:128 #: templates/dcim/device_edit.html:10 templates/dcim/devicebay.html:23 @@ -2824,7 +2822,7 @@ msgstr "プラットフォーム" #: virtualization/forms/bulk_import.py:99 #: virtualization/forms/filtersets.py:124 #: virtualization/forms/model_forms.py:188 -#: virtualization/tables/virtualmachines.py:61 vpn/choices.py:44 +#: virtualization/tables/virtualmachines.py:70 vpn/choices.py:44 #: vpn/forms/bulk_import.py:86 vpn/forms/bulk_import.py:283 #: vpn/forms/filtersets.py:271 vpn/forms/model_forms.py:89 #: vpn/forms/model_forms.py:124 vpn/forms/model_forms.py:237 @@ -2964,7 +2962,7 @@ msgid "Wireless role" msgstr "無線ロール" #: dcim/forms/bulk_edit.py:1178 dcim/forms/model_forms.py:595 -#: dcim/forms/model_forms.py:1026 dcim/tables/devices.py:337 +#: dcim/forms/model_forms.py:1026 dcim/tables/devices.py:341 #: templates/dcim/consoleport.html:27 templates/dcim/consoleserverport.html:27 #: templates/dcim/frontport.html:27 templates/dcim/interface.html:35 #: templates/dcim/module.html:51 templates/dcim/modulebay.html:57 @@ -2973,7 +2971,7 @@ msgstr "無線ロール" msgid "Module" msgstr "モジュール" -#: dcim/forms/bulk_edit.py:1305 dcim/tables/devices.py:685 +#: dcim/forms/bulk_edit.py:1305 dcim/tables/devices.py:689 #: templates/dcim/interface.html:113 msgid "LAG" msgstr "LAG" @@ -2985,7 +2983,7 @@ msgstr "仮想デバイスコンテキスト" #: dcim/forms/bulk_edit.py:1316 dcim/forms/bulk_import.py:659 #: dcim/forms/bulk_import.py:685 dcim/forms/filtersets.py:1163 #: dcim/forms/filtersets.py:1185 dcim/forms/filtersets.py:1258 -#: dcim/tables/devices.py:626 +#: dcim/tables/devices.py:630 #: templates/circuits/inc/circuit_termination.html:94 #: templates/dcim/consoleport.html:43 templates/dcim/consoleserverport.html:43 msgid "Speed" @@ -3010,13 +3008,13 @@ msgid "VLAN group" msgstr "VLAN グループ" #: dcim/forms/bulk_edit.py:1361 dcim/forms/model_forms.py:1164 -#: dcim/tables/devices.py:599 virtualization/forms/bulk_edit.py:247 +#: dcim/tables/devices.py:603 virtualization/forms/bulk_edit.py:247 #: virtualization/forms/model_forms.py:329 msgid "Untagged VLAN" msgstr "タグなし VLAN" #: dcim/forms/bulk_edit.py:1369 dcim/forms/model_forms.py:1173 -#: dcim/tables/devices.py:605 virtualization/forms/bulk_edit.py:255 +#: dcim/tables/devices.py:609 virtualization/forms/bulk_edit.py:255 #: virtualization/forms/model_forms.py:338 msgid "Tagged VLANs" msgstr "タグ付き VLAN" @@ -3026,7 +3024,7 @@ msgid "Wireless LAN group" msgstr "無線 LAN グループ" #: dcim/forms/bulk_edit.py:1384 dcim/forms/model_forms.py:1151 -#: dcim/tables/devices.py:635 netbox/navigation/menu.py:134 +#: dcim/tables/devices.py:639 netbox/navigation/menu.py:134 #: templates/dcim/interface.html:289 wireless/tables/wirelesslan.py:24 msgid "Wireless LANs" msgstr "無線 LAN" @@ -3197,9 +3195,9 @@ msgid "Virtual chassis" msgstr "バーチャルシャーシ" #: dcim/forms/bulk_import.py:462 dcim/forms/model_forms.py:457 -#: dcim/tables/devices.py:231 extras/filtersets.py:501 +#: dcim/tables/devices.py:235 extras/filtersets.py:501 #: extras/forms/filtersets.py:330 ipam/forms/bulk_edit.py:478 -#: ipam/forms/model_forms.py:588 templates/dcim/device.html:239 +#: ipam/forms/model_forms.py:592 templates/dcim/device.html:239 #: templates/virtualization/cluster.html:11 #: templates/virtualization/virtualmachine.html:92 #: templates/virtualization/virtualmachine.html:102 @@ -3211,7 +3209,7 @@ msgstr "バーチャルシャーシ" #: virtualization/forms/filtersets.py:196 #: virtualization/forms/model_forms.py:82 #: virtualization/forms/model_forms.py:179 -#: virtualization/tables/virtualmachines.py:57 +#: virtualization/tables/virtualmachines.py:66 msgid "Cluster" msgstr "クラスタ" @@ -3388,7 +3386,7 @@ msgstr "対応する背面ポート" msgid "Physical medium classification" msgstr "物理媒体の分類" -#: dcim/forms/bulk_import.py:973 dcim/tables/devices.py:846 +#: dcim/forms/bulk_import.py:973 dcim/tables/devices.py:850 msgid "Installed device" msgstr "取付済みデバイス" @@ -3476,7 +3474,7 @@ msgid "{side_upper} side termination not found: {device} {name}" msgstr "{side_upper} サイドターミネーションが見つかりません: {device} {name}" #: dcim/forms/bulk_import.py:1244 dcim/forms/model_forms.py:696 -#: dcim/tables/devices.py:1033 templates/dcim/device.html:130 +#: dcim/tables/devices.py:1037 templates/dcim/device.html:130 #: templates/dcim/virtualchassis.html:28 templates/dcim/virtualchassis.html:60 msgid "Master" msgstr "マスター" @@ -3598,7 +3596,7 @@ msgstr "専有済" #: dcim/forms/filtersets.py:1155 dcim/forms/filtersets.py:1177 #: dcim/forms/filtersets.py:1199 dcim/forms/filtersets.py:1216 -#: dcim/forms/filtersets.py:1236 dcim/tables/devices.py:372 +#: dcim/forms/filtersets.py:1236 dcim/tables/devices.py:376 #: templates/dcim/consoleport.html:59 templates/dcim/consoleserverport.html:59 #: templates/dcim/frontport.html:74 templates/dcim/interface.html:146 #: templates/dcim/powerfeed.html:118 templates/dcim/poweroutlet.html:63 @@ -3612,7 +3610,7 @@ msgid "Virtual Device Context" msgstr "仮想デバイスコンテキスト" #: dcim/forms/filtersets.py:1248 extras/forms/bulk_edit.py:315 -#: extras/forms/bulk_import.py:239 extras/forms/filtersets.py:479 +#: extras/forms/bulk_import.py:245 extras/forms/filtersets.py:479 #: extras/forms/model_forms.py:557 extras/tables/tables.py:487 #: templates/extras/journalentry.html:33 msgid "Kind" @@ -3644,7 +3642,7 @@ msgid "Transmit power (dBm)" msgstr "送信出力 (dBm)" #: dcim/forms/filtersets.py:1344 dcim/forms/filtersets.py:1366 -#: dcim/tables/devices.py:344 templates/dcim/cable.html:12 +#: dcim/tables/devices.py:348 templates/dcim/cable.html:12 #: templates/dcim/cable_edit.html:46 templates/dcim/cable_trace.html:43 #: templates/dcim/frontport.html:84 #: templates/dcim/inc/connection_endpoints.html:4 @@ -3652,7 +3650,7 @@ msgstr "送信出力 (dBm)" msgid "Cable" msgstr "ケーブル" -#: dcim/forms/filtersets.py:1434 dcim/tables/devices.py:956 +#: dcim/forms/filtersets.py:1434 dcim/tables/devices.py:960 msgid "Discovered" msgstr "自動検出" @@ -3697,7 +3695,7 @@ msgstr "シャーシ" msgid "Device Role" msgstr "デバイスロール" -#: dcim/forms/model_forms.py:428 dcim/models/devices.py:632 +#: dcim/forms/model_forms.py:428 dcim/models/devices.py:634 msgid "The lowest-numbered unit occupied by the device" msgstr "デバイスが使用している最も小さいユニット番号" @@ -3748,9 +3746,7 @@ msgstr "LAG インタフェース" #: templates/wireless/wirelesslink.html:10 #: templates/wireless/wirelesslink.html:49 #: virtualization/forms/model_forms.py:351 vpn/forms/bulk_import.py:297 -#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 -#: vpn/forms/model_forms.py:241 vpn/forms/model_forms.py:436 -#: vpn/forms/model_forms.py:445 vpn/tables/tunnels.py:91 +#: vpn/forms/model_forms.py:436 vpn/forms/model_forms.py:445 #: wireless/forms/model_forms.py:112 wireless/forms/model_forms.py:152 msgid "Interface" msgstr "インタフェース" @@ -3821,7 +3817,7 @@ msgid "" msgstr "パターンは {value_count} 個の値を示す範囲を指定しますが、 {pattern_count} 個の値が必要です。" #: dcim/forms/object_create.py:109 dcim/forms/object_create.py:270 -#: dcim/tables/devices.py:281 +#: dcim/tables/devices.py:285 msgid "Rear ports" msgstr "背面ポート" @@ -3853,7 +3849,7 @@ msgid "" msgstr "" "前面ポートの数 ({frontport_count}) は選択した背面ポートの数 ({rearport_count}) と一致する必要があります。" -#: dcim/forms/object_create.py:408 dcim/tables/devices.py:1039 +#: dcim/forms/object_create.py:408 dcim/tables/devices.py:1043 #: ipam/tables/fhrp.py:31 templates/dcim/virtualchassis.html:54 #: templates/dcim/virtualchassis_edit.html:48 templates/ipam/fhrpgroup.html:39 msgid "Members" @@ -4545,13 +4541,13 @@ msgstr "在庫品目ロール" msgid "inventory item roles" msgstr "在庫品目ロール" -#: dcim/models/device_components.py:1230 dcim/models/devices.py:595 -#: dcim/models/devices.py:1173 dcim/models/racks.py:113 +#: dcim/models/device_components.py:1230 dcim/models/devices.py:597 +#: dcim/models/devices.py:1178 dcim/models/racks.py:113 msgid "serial number" msgstr "シリアル番号" -#: dcim/models/device_components.py:1238 dcim/models/devices.py:603 -#: dcim/models/devices.py:1180 dcim/models/racks.py:120 +#: dcim/models/device_components.py:1238 dcim/models/devices.py:605 +#: dcim/models/devices.py:1185 dcim/models/racks.py:120 msgid "asset tag" msgstr "アセットタグ" @@ -4599,7 +4595,7 @@ msgstr "メーカ" msgid "manufacturers" msgstr "メーカ" -#: dcim/models/devices.py:82 dcim/models/devices.py:381 +#: dcim/models/devices.py:82 dcim/models/devices.py:382 msgid "model" msgstr "型" @@ -4607,11 +4603,11 @@ msgstr "型" msgid "default platform" msgstr "デフォルトプラットフォーム" -#: dcim/models/devices.py:98 dcim/models/devices.py:385 +#: dcim/models/devices.py:98 dcim/models/devices.py:386 msgid "part number" msgstr "パーツ番号" -#: dcim/models/devices.py:101 dcim/models/devices.py:388 +#: dcim/models/devices.py:101 dcim/models/devices.py:389 msgid "Discrete part number (optional)" msgstr "個別の部品番号 (オプション)" @@ -4645,7 +4641,7 @@ msgid "" "device type is neither a parent nor a child." msgstr "親デバイスはデバイスベイに子デバイスを収納します。このデバイスタイプが親でも子供でもない場合は、空白のままにしてください。" -#: dcim/models/devices.py:128 dcim/models/devices.py:647 +#: dcim/models/devices.py:128 dcim/models/devices.py:649 msgid "airflow" msgstr "エアフロー" @@ -4657,18 +4653,18 @@ msgstr "デバイスタイプ" msgid "device types" msgstr "デバイスタイプ" -#: dcim/models/devices.py:289 +#: dcim/models/devices.py:290 msgid "U height must be in increments of 0.5 rack units." msgstr "U の高さは 0.5 ラック単位でなければなりません。" -#: dcim/models/devices.py:306 +#: dcim/models/devices.py:307 #, python-brace-format msgid "" "Device {device} in rack {rack} does not have sufficient space to accommodate" " a height of {height}U" msgstr "ラック内 {rack} のデバイス {device} は高さ{height}Uに対応する十分なスペースが有りません " -#: dcim/models/devices.py:321 +#: dcim/models/devices.py:322 #, python-brace-format msgid "" "Unable to set 0U height: Found {racked_instance_count} " @@ -4677,173 +4673,173 @@ msgstr "" "高さは 0U にできません: {racked_instance_count} インスタンス " "がラックに取り付け済みです。" -#: dcim/models/devices.py:330 +#: dcim/models/devices.py:331 msgid "" "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." msgstr "このデバイスを親デバイスとして分類解除する前に、このデバイスに関連付けられているすべてのデバイスベイテンプレートを削除する必要があります。" -#: dcim/models/devices.py:336 +#: dcim/models/devices.py:337 msgid "Child device types must be 0U." msgstr "子デバイスタイプは 0U でなければなりません。" -#: dcim/models/devices.py:404 +#: dcim/models/devices.py:405 msgid "module type" msgstr "モジュールタイプ" -#: dcim/models/devices.py:405 +#: dcim/models/devices.py:406 msgid "module types" msgstr "モジュールタイプ" -#: dcim/models/devices.py:473 +#: dcim/models/devices.py:475 msgid "Virtual machines may be assigned to this role" msgstr "仮想マシンをこのロールに割り当てることができます" -#: dcim/models/devices.py:485 +#: dcim/models/devices.py:487 msgid "device role" msgstr "デバイスロール" -#: dcim/models/devices.py:486 +#: dcim/models/devices.py:488 msgid "device roles" msgstr "デバイスロール" -#: dcim/models/devices.py:503 +#: dcim/models/devices.py:505 msgid "Optionally limit this platform to devices of a certain manufacturer" msgstr "オプションで、このプラットフォームを特定のメーカのデバイスに限定できます" -#: dcim/models/devices.py:515 +#: dcim/models/devices.py:517 msgid "platform" msgstr "プラットフォーム" -#: dcim/models/devices.py:516 +#: dcim/models/devices.py:518 msgid "platforms" msgstr "プラットフォーム" -#: dcim/models/devices.py:564 +#: dcim/models/devices.py:566 msgid "The function this device serves" msgstr "このデバイスが果たす機能" -#: dcim/models/devices.py:596 +#: dcim/models/devices.py:598 msgid "Chassis serial number, assigned by the manufacturer" msgstr "製造元によって割当られた、シャーシのシリアル番号" -#: dcim/models/devices.py:604 dcim/models/devices.py:1181 +#: dcim/models/devices.py:606 dcim/models/devices.py:1186 msgid "A unique tag used to identify this device" msgstr "このデバイスを識別するために使用される一意のタグ" -#: dcim/models/devices.py:631 +#: dcim/models/devices.py:633 msgid "position (U)" msgstr "ポジション (U)" -#: dcim/models/devices.py:638 +#: dcim/models/devices.py:640 msgid "rack face" msgstr "ラックフェイス" -#: dcim/models/devices.py:658 dcim/models/devices.py:1390 +#: dcim/models/devices.py:660 dcim/models/devices.py:1395 #: virtualization/models/virtualmachines.py:98 msgid "primary IPv4" msgstr "プライマリ IPv4" -#: dcim/models/devices.py:666 dcim/models/devices.py:1398 +#: dcim/models/devices.py:668 dcim/models/devices.py:1403 #: virtualization/models/virtualmachines.py:106 msgid "primary IPv6" msgstr "プライマリ IPv6" -#: dcim/models/devices.py:674 +#: dcim/models/devices.py:676 msgid "out-of-band IP" msgstr "out-of-band IP" -#: dcim/models/devices.py:691 +#: dcim/models/devices.py:693 msgid "VC position" msgstr "VCポジション" -#: dcim/models/devices.py:695 +#: dcim/models/devices.py:697 msgid "Virtual chassis position" msgstr "バーチャルシャーシポジション" -#: dcim/models/devices.py:698 +#: dcim/models/devices.py:700 msgid "VC priority" msgstr "VC プライオリティ" -#: dcim/models/devices.py:702 +#: dcim/models/devices.py:704 msgid "Virtual chassis master election priority" msgstr "バーチャルシャーシのマスター選択優先順位" -#: dcim/models/devices.py:705 dcim/models/sites.py:207 +#: dcim/models/devices.py:707 dcim/models/sites.py:207 msgid "latitude" msgstr "緯度" -#: dcim/models/devices.py:710 dcim/models/devices.py:718 +#: dcim/models/devices.py:712 dcim/models/devices.py:720 #: dcim/models/sites.py:212 dcim/models/sites.py:220 msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgstr "10 進数形式の GPS 座標 (xx.yyyyyy)" -#: dcim/models/devices.py:713 dcim/models/sites.py:215 +#: dcim/models/devices.py:715 dcim/models/sites.py:215 msgid "longitude" msgstr "経度" -#: dcim/models/devices.py:786 +#: dcim/models/devices.py:788 msgid "Device name must be unique per site." msgstr "デバイス名はサイトごとに一意である必要があります。" -#: dcim/models/devices.py:797 ipam/models/services.py:75 +#: dcim/models/devices.py:799 ipam/models/services.py:75 msgid "device" msgstr "デバイス" -#: dcim/models/devices.py:798 +#: dcim/models/devices.py:800 msgid "devices" msgstr "デバイス" -#: dcim/models/devices.py:838 +#: dcim/models/devices.py:840 #, python-brace-format msgid "Rack {rack} does not belong to site {site}." msgstr "ラック {rack} はサイト{site}に属していません 。" -#: dcim/models/devices.py:843 +#: dcim/models/devices.py:845 #, python-brace-format msgid "Location {location} does not belong to site {site}." msgstr "ロケーション {location} はサイト{site}に属していません 。" -#: dcim/models/devices.py:849 +#: dcim/models/devices.py:851 #, python-brace-format msgid "Rack {rack} does not belong to location {location}." msgstr "ラック {rack} はロケーション{location}に属していません 。" -#: dcim/models/devices.py:856 +#: dcim/models/devices.py:858 msgid "Cannot select a rack face without assigning a rack." msgstr "ラックを割り当てないとラックフェースは選択できません。" -#: dcim/models/devices.py:860 +#: dcim/models/devices.py:862 msgid "Cannot select a rack position without assigning a rack." msgstr "ラックを割り当てないとラックポジションを選択できません。" -#: dcim/models/devices.py:866 +#: dcim/models/devices.py:868 msgid "Position must be in increments of 0.5 rack units." msgstr "ポジションは 0.5 ラックユニット単位で入力する必要があります。" -#: dcim/models/devices.py:870 +#: dcim/models/devices.py:872 msgid "Must specify rack face when defining rack position." msgstr "ラックの位置を定義するときは、ラックの面を指定する必要があります。" -#: dcim/models/devices.py:878 +#: dcim/models/devices.py:880 #, python-brace-format msgid "" "A 0U device type ({device_type}) cannot be assigned to a rack position." msgstr "0U デバイスタイプ ({device_type}) をラックポジションに割り当てることはできません。" -#: dcim/models/devices.py:889 +#: dcim/models/devices.py:891 msgid "" "Child device types cannot be assigned to a rack face. This is an attribute " "of the parent device." msgstr "子デバイスタイプをラックフェースに割り当てることはできません。これは親デバイスの属性です。" -#: dcim/models/devices.py:896 +#: dcim/models/devices.py:898 msgid "" "Child device types cannot be assigned to a rack position. This is an " "attribute of the parent device." msgstr "子デバイスタイプをラックポジションに割り当てることはできません。これは親デバイスの属性です。" -#: dcim/models/devices.py:910 +#: dcim/models/devices.py:912 #, python-brace-format msgid "" "U{position} is already occupied or does not have sufficient space to " @@ -4852,22 +4848,22 @@ msgstr "" "U{position} が既に占有されているか、このデバイスタイプを収容するのに十分なスペースがありません: {device_type} " "({u_height}U)" -#: dcim/models/devices.py:925 +#: dcim/models/devices.py:927 #, python-brace-format msgid "{ip} is not an IPv4 address." msgstr "{ip} は IPv4 アドレスではありません。" -#: dcim/models/devices.py:934 dcim/models/devices.py:949 +#: dcim/models/devices.py:936 dcim/models/devices.py:951 #, python-brace-format msgid "The specified IP address ({ip}) is not assigned to this device." msgstr "指定された IP アドレス ({ip}) はこのデバイスに割り当てられていません。" -#: dcim/models/devices.py:940 +#: dcim/models/devices.py:942 #, python-brace-format msgid "{ip} is not an IPv6 address." msgstr "{ip} IPv6 アドレスではありません。" -#: dcim/models/devices.py:967 +#: dcim/models/devices.py:969 #, python-brace-format msgid "" "The assigned platform is limited to {platform_manufacturer} device types, " @@ -4876,78 +4872,78 @@ msgstr "" "割当られたプラットフォームは{platform_manufacturer} のデバイスタイプに限定されます 。しかし、このデバイスのタイプは " "{devicetype_manufacturer}に属します。" -#: dcim/models/devices.py:978 +#: dcim/models/devices.py:980 #, python-brace-format msgid "The assigned cluster belongs to a different site ({site})" msgstr "割当クラスタは別のサイトに属しています ({site})" -#: dcim/models/devices.py:986 +#: dcim/models/devices.py:988 msgid "A device assigned to a virtual chassis must have its position defined." msgstr "仮想シャーシに割当られたデバイスには、その位置が定義されている必要があります。" -#: dcim/models/devices.py:1188 +#: dcim/models/devices.py:1193 msgid "module" msgstr "モジュール" -#: dcim/models/devices.py:1189 +#: dcim/models/devices.py:1194 msgid "modules" msgstr "モジュール" -#: dcim/models/devices.py:1205 +#: dcim/models/devices.py:1210 #, python-brace-format msgid "" "Module must be installed within a module bay belonging to the assigned " "device ({device})." msgstr "モジュールは、割当デバイスに属するモジュールベイ内に取り付ける必要があります ({device})。" -#: dcim/models/devices.py:1309 +#: dcim/models/devices.py:1314 msgid "domain" msgstr "ドメイン" -#: dcim/models/devices.py:1322 dcim/models/devices.py:1323 +#: dcim/models/devices.py:1327 dcim/models/devices.py:1328 msgid "virtual chassis" msgstr "バーチャルシャーシ" -#: dcim/models/devices.py:1338 +#: dcim/models/devices.py:1343 #, python-brace-format msgid "" "The selected master ({master}) is not assigned to this virtual chassis." msgstr "選択したマスター ({master}) はこの仮想シャーシに割り当てられていません。" -#: dcim/models/devices.py:1354 +#: dcim/models/devices.py:1359 #, python-brace-format msgid "" "Unable to delete virtual chassis {self}. There are member interfaces which " "form a cross-chassis LAG interfaces." msgstr "バーチャルシャーシ{self}を削除できません 。クロスシャーシ LAG インタフェースを形成するメンバーインタフェースがあります。" -#: dcim/models/devices.py:1379 vpn/models/l2vpn.py:37 +#: dcim/models/devices.py:1384 vpn/models/l2vpn.py:37 msgid "identifier" msgstr "識別子" -#: dcim/models/devices.py:1380 +#: dcim/models/devices.py:1385 msgid "Numeric identifier unique to the parent device" msgstr "親デバイスに固有の数値識別子" -#: dcim/models/devices.py:1408 extras/models/models.py:129 +#: dcim/models/devices.py:1413 extras/models/models.py:129 #: extras/models/models.py:724 netbox/models/__init__.py:114 msgid "comments" msgstr "コメント" -#: dcim/models/devices.py:1424 +#: dcim/models/devices.py:1429 msgid "virtual device context" msgstr "仮想デバイスコンテキスト" -#: dcim/models/devices.py:1425 +#: dcim/models/devices.py:1430 msgid "virtual device contexts" msgstr "仮想デバイスコンテキスト" -#: dcim/models/devices.py:1457 +#: dcim/models/devices.py:1462 #, python-brace-format msgid "{ip} is not an IPv{family} address." msgstr "{ip}は IPv{family}アドレスではありません。" -#: dcim/models/devices.py:1463 +#: dcim/models/devices.py:1468 msgid "Primary IP address must belong to an interface on the assigned device." msgstr "プライマリ IP アドレスは、割当デバイスのインタフェースに属している必要があります。" @@ -5322,7 +5318,7 @@ msgstr "コンソールポート" msgid "Reachable" msgstr "到達可能" -#: dcim/tables/connections.py:46 dcim/tables/devices.py:529 +#: dcim/tables/connections.py:46 dcim/tables/devices.py:533 #: templates/dcim/inventoryitem_edit.html:64 #: templates/dcim/poweroutlet.html:47 templates/dcim/powerport.html:18 msgid "Power Port" @@ -5341,7 +5337,7 @@ msgstr "デバイス" msgid "VMs" msgstr "仮想マシン" -#: dcim/tables/devices.py:133 dcim/tables/devices.py:245 +#: dcim/tables/devices.py:133 dcim/tables/devices.py:249 #: extras/forms/model_forms.py:515 templates/dcim/device.html:114 #: templates/dcim/device/render_config.html:11 #: templates/dcim/device/render_config.html:15 @@ -5350,62 +5346,62 @@ msgstr "仮想マシン" #: templates/virtualization/virtualmachine.html:47 #: templates/virtualization/virtualmachine/render_config.html:11 #: templates/virtualization/virtualmachine/render_config.html:15 -#: virtualization/tables/virtualmachines.py:93 +#: virtualization/tables/virtualmachines.py:106 msgid "Config Template" msgstr "設定テンプレート" -#: dcim/tables/devices.py:216 dcim/tables/devices.py:1074 +#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 #: ipam/forms/bulk_import.py:511 ipam/forms/model_forms.py:296 #: ipam/tables/ip.py:352 ipam/tables/ip.py:418 ipam/tables/ip.py:441 #: templates/ipam/ipaddress.html:12 templates/ipam/ipaddress_edit.html:14 -#: virtualization/tables/virtualmachines.py:81 +#: virtualization/tables/virtualmachines.py:94 msgid "IP Address" msgstr "IP アドレス" -#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 -#: virtualization/tables/virtualmachines.py:72 +#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 +#: virtualization/tables/virtualmachines.py:85 msgid "IPv4 Address" msgstr "IPv4 アドレス" -#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 -#: virtualization/tables/virtualmachines.py:76 +#: dcim/tables/devices.py:228 dcim/tables/devices.py:1086 +#: virtualization/tables/virtualmachines.py:89 msgid "IPv6 Address" msgstr "IPv6 アドレス" -#: dcim/tables/devices.py:239 +#: dcim/tables/devices.py:243 msgid "VC Position" msgstr "VC ポジション" -#: dcim/tables/devices.py:242 +#: dcim/tables/devices.py:246 msgid "VC Priority" msgstr "VC プライオリティ" -#: dcim/tables/devices.py:249 templates/dcim/device_edit.html:38 +#: dcim/tables/devices.py:253 templates/dcim/device_edit.html:38 #: templates/dcim/devicebay_populate.html:16 msgid "Parent Device" msgstr "親デバイス" -#: dcim/tables/devices.py:254 +#: dcim/tables/devices.py:258 msgid "Position (Device Bay)" msgstr "位置 (デバイスベイ)" -#: dcim/tables/devices.py:263 +#: dcim/tables/devices.py:267 msgid "Console ports" msgstr "コンソールポート" -#: dcim/tables/devices.py:266 +#: dcim/tables/devices.py:270 msgid "Console server ports" msgstr "コンソールサーバポート" -#: dcim/tables/devices.py:269 +#: dcim/tables/devices.py:273 msgid "Power ports" msgstr "電源ポート" -#: dcim/tables/devices.py:272 +#: dcim/tables/devices.py:276 msgid "Power outlets" msgstr "電源コンセント" -#: dcim/tables/devices.py:275 dcim/tables/devices.py:1087 +#: dcim/tables/devices.py:279 dcim/tables/devices.py:1091 #: dcim/tables/devicetypes.py:125 dcim/views.py:1005 dcim/views.py:1244 #: dcim/views.py:1930 netbox/navigation/menu.py:82 #: netbox/navigation/menu.py:238 templates/dcim/device/base.html:37 @@ -5415,53 +5411,53 @@ msgstr "電源コンセント" #: templates/dcim/virtualdevicecontext.html:85 #: templates/virtualization/virtualmachine/base.html:27 #: templates/virtualization/virtualmachine_list.html:14 -#: virtualization/tables/virtualmachines.py:87 virtualization/views.py:368 +#: virtualization/tables/virtualmachines.py:100 virtualization/views.py:368 #: wireless/tables/wirelesslan.py:55 msgid "Interfaces" msgstr "インタフェース" -#: dcim/tables/devices.py:278 +#: dcim/tables/devices.py:282 msgid "Front ports" msgstr "前面ポート" -#: dcim/tables/devices.py:284 +#: dcim/tables/devices.py:288 msgid "Device bays" msgstr "デバイスベイ" -#: dcim/tables/devices.py:287 +#: dcim/tables/devices.py:291 msgid "Module bays" msgstr "モジュールベイ" -#: dcim/tables/devices.py:290 +#: dcim/tables/devices.py:294 msgid "Inventory items" msgstr "在庫品目" -#: dcim/tables/devices.py:329 dcim/tables/modules.py:56 +#: dcim/tables/devices.py:333 dcim/tables/modules.py:56 #: templates/dcim/modulebay.html:17 msgid "Module Bay" msgstr "モジュールベイ" -#: dcim/tables/devices.py:350 +#: dcim/tables/devices.py:354 msgid "Cable Color" msgstr "ケーブル色" -#: dcim/tables/devices.py:356 +#: dcim/tables/devices.py:360 msgid "Link Peers" msgstr "対向" -#: dcim/tables/devices.py:359 +#: dcim/tables/devices.py:363 msgid "Mark Connected" msgstr "接続済みとしてマークする" -#: dcim/tables/devices.py:475 +#: dcim/tables/devices.py:479 msgid "Maximum draw (W)" msgstr "最大電力 (W)" -#: dcim/tables/devices.py:478 +#: dcim/tables/devices.py:482 msgid "Allocated draw (W)" msgstr "割当電力 (W)" -#: dcim/tables/devices.py:578 ipam/forms/model_forms.py:707 +#: dcim/tables/devices.py:582 ipam/forms/model_forms.py:711 #: ipam/tables/fhrp.py:28 ipam/views.py:597 ipam/views.py:691 #: netbox/navigation/menu.py:146 netbox/navigation/menu.py:148 #: templates/dcim/interface.html:351 templates/ipam/ipaddress_bulk_add.html:15 @@ -5470,12 +5466,12 @@ msgstr "割当電力 (W)" msgid "IP Addresses" msgstr "IP アドレス" -#: dcim/tables/devices.py:584 netbox/navigation/menu.py:190 +#: dcim/tables/devices.py:588 netbox/navigation/menu.py:190 #: templates/ipam/inc/panels/fhrp_groups.html:5 msgid "FHRP Groups" msgstr "FHRP グループ" -#: dcim/tables/devices.py:596 templates/dcim/interface.html:90 +#: dcim/tables/devices.py:600 templates/dcim/interface.html:90 #: templates/virtualization/vminterface.html:70 templates/vpn/tunnel.html:18 #: templates/vpn/tunneltermination.html:14 vpn/forms/bulk_edit.py:75 #: vpn/forms/bulk_import.py:76 vpn/forms/filtersets.py:41 @@ -5484,20 +5480,20 @@ msgstr "FHRP グループ" msgid "Tunnel" msgstr "トンネル" -#: dcim/tables/devices.py:621 dcim/tables/devicetypes.py:224 +#: dcim/tables/devices.py:625 dcim/tables/devicetypes.py:224 #: templates/dcim/interface.html:66 msgid "Management Only" msgstr "管理のみ" -#: dcim/tables/devices.py:629 +#: dcim/tables/devices.py:633 msgid "Wireless link" msgstr "無線リンク" -#: dcim/tables/devices.py:639 +#: dcim/tables/devices.py:643 msgid "VDCs" msgstr "VDC" -#: dcim/tables/devices.py:647 dcim/tables/devicetypes.py:48 +#: dcim/tables/devices.py:651 dcim/tables/devicetypes.py:48 #: dcim/tables/devicetypes.py:140 dcim/views.py:1080 dcim/views.py:2023 #: netbox/navigation/menu.py:91 templates/dcim/device/base.html:52 #: templates/dcim/device_list.html:71 templates/dcim/devicetype/base.html:49 @@ -5506,7 +5502,7 @@ msgstr "VDC" msgid "Inventory Items" msgstr "在庫品目" -#: dcim/tables/devices.py:728 +#: dcim/tables/devices.py:732 #: templates/circuits/inc/circuit_termination.html:80 #: templates/dcim/consoleport.html:81 templates/dcim/consoleserverport.html:81 #: templates/dcim/frontport.html:53 templates/dcim/frontport.html:125 @@ -5515,28 +5511,28 @@ msgstr "在庫品目" msgid "Rear Port" msgstr "背面ポート" -#: dcim/tables/devices.py:893 templates/dcim/modulebay.html:51 +#: dcim/tables/devices.py:897 templates/dcim/modulebay.html:51 msgid "Installed Module" msgstr "取付済みモジュール" -#: dcim/tables/devices.py:896 +#: dcim/tables/devices.py:900 msgid "Module Serial" msgstr "モジュールシリアル番号" -#: dcim/tables/devices.py:900 +#: dcim/tables/devices.py:904 msgid "Module Asset Tag" msgstr "モジュール資産タグ" -#: dcim/tables/devices.py:909 +#: dcim/tables/devices.py:913 msgid "Module Status" msgstr "モジュールステータス" -#: dcim/tables/devices.py:951 dcim/tables/devicetypes.py:308 +#: dcim/tables/devices.py:955 dcim/tables/devicetypes.py:308 #: templates/dcim/inventoryitem.html:41 msgid "Component" msgstr "構成要素" -#: dcim/tables/devices.py:1006 +#: dcim/tables/devices.py:1010 msgid "Items" msgstr "アイテム" @@ -6094,7 +6090,7 @@ msgid "Cluster type (slug)" msgstr "クラスタタイプ (slug)" #: extras/filtersets.py:490 ipam/forms/bulk_edit.py:475 -#: ipam/forms/model_forms.py:585 virtualization/forms/filtersets.py:108 +#: ipam/forms/model_forms.py:589 virtualization/forms/filtersets.py:108 msgid "Cluster group" msgstr "クラスタグループ" @@ -6225,8 +6221,8 @@ msgid "Is active" msgstr "有効" #: extras/forms/bulk_import.py:34 extras/forms/bulk_import.py:115 -#: extras/forms/bulk_import.py:130 extras/forms/bulk_import.py:153 -#: extras/forms/bulk_import.py:177 extras/forms/filtersets.py:114 +#: extras/forms/bulk_import.py:136 extras/forms/bulk_import.py:159 +#: extras/forms/bulk_import.py:183 extras/forms/filtersets.py:114 #: extras/forms/filtersets.py:160 extras/forms/filtersets.py:201 #: extras/forms/model_forms.py:43 extras/forms/model_forms.py:127 #: extras/forms/model_forms.py:156 extras/forms/model_forms.py:197 @@ -6235,8 +6231,8 @@ msgid "Content types" msgstr "コンテンツタイプ" #: extras/forms/bulk_import.py:36 extras/forms/bulk_import.py:117 -#: extras/forms/bulk_import.py:132 extras/forms/bulk_import.py:155 -#: extras/forms/bulk_import.py:179 tenancy/forms/bulk_import.py:96 +#: extras/forms/bulk_import.py:138 extras/forms/bulk_import.py:161 +#: extras/forms/bulk_import.py:185 tenancy/forms/bulk_import.py:96 msgid "One or more assigned object types" msgstr "1 つ以上の割当オブジェクトタイプ" @@ -6278,33 +6274,44 @@ msgstr "定義済みの選択肢の基本セット (存在する場合)" msgid "" "Quoted string of comma-separated field choices with optional labels " "separated by colon: \"choice1:First Choice,choice2:Second Choice\"" -msgstr "カンマで区切られたフィールド選択肢とコロンで区切られたオプションのラベルを引用符で囲んだ文字列:「選択肢1:第一選択、選択肢2:第二選択」" +msgstr "" +"引用符で囲んだ、カンマ区切りの選択肢。コロン区切りでラベル設定可能: \"choice1:First Choice,choice2:Second " +"Choice\"" -#: extras/forms/bulk_import.py:182 +#: extras/forms/bulk_import.py:120 extras/models/models.py:353 +msgid "button class" +msgstr "ボタンクラス" + +#: extras/forms/bulk_import.py:123 extras/models/models.py:357 +msgid "" +"The class of the first link in a group will be used for the dropdown button" +msgstr "グループ内の最初のリンクのクラスがドロップダウンボタンに使用されます" + +#: extras/forms/bulk_import.py:188 msgid "Action object" msgstr "アクションオブジェクト" -#: extras/forms/bulk_import.py:184 +#: extras/forms/bulk_import.py:190 msgid "Webhook name or script as dotted path module.Class" -msgstr "ドットパス形式のウェブフック名またはスクリプト module.Class" +msgstr "ドットパス形式 (module.Class) のウェブフック名またはスクリプト" -#: extras/forms/bulk_import.py:205 +#: extras/forms/bulk_import.py:211 #, python-brace-format msgid "Webhook {name} not found" msgstr "ウェブフック {name} 見つかりません" -#: extras/forms/bulk_import.py:214 +#: extras/forms/bulk_import.py:220 #, python-brace-format msgid "Script {name} not found" msgstr "スクリプト {name} 見つかりません" -#: extras/forms/bulk_import.py:236 +#: extras/forms/bulk_import.py:242 msgid "Assigned object type" msgstr "割当オブジェクトタイプ" -#: extras/forms/bulk_import.py:241 +#: extras/forms/bulk_import.py:247 msgid "The classification of entry" -msgstr "エントリーの分類" +msgstr "エントリの分類" #: extras/forms/filtersets.py:53 msgid "Field type" @@ -6320,7 +6327,7 @@ msgstr "選択肢" #: templates/core/job.html:86 templates/extras/configcontext.html:86 #: templates/extras/eventrule.html:111 msgid "Data" -msgstr "[データ]" +msgstr "データ" #: extras/forms/filtersets.py:152 extras/forms/filtersets.py:341 #: extras/forms/filtersets.py:427 utilities/choices.py:219 @@ -6330,11 +6337,11 @@ msgstr "データファイル" #: extras/forms/filtersets.py:185 msgid "Content type" -msgstr "コンテンツタイプ" +msgstr "Content type" #: extras/forms/filtersets.py:232 extras/models/models.py:209 msgid "HTTP content type" -msgstr "HTTP コンテンツタイプ" +msgstr "HTTP content type" #: extras/forms/filtersets.py:254 extras/forms/model_forms.py:271 #: templates/extras/eventrule.html:46 @@ -6359,7 +6366,7 @@ msgstr "オブジェクト削除" #: extras/forms/filtersets.py:299 msgid "Job starts" -msgstr "ジョブ開始" +msgstr "ジョブの開始" #: extras/forms/filtersets.py:306 extras/forms/model_forms.py:290 msgid "Job terminations" @@ -6416,11 +6423,11 @@ msgstr "テナントグループ" #: extras/forms/filtersets.py:454 extras/forms/filtersets.py:495 msgid "After" -msgstr "後" +msgstr "以降" #: extras/forms/filtersets.py:459 extras/forms/filtersets.py:500 msgid "Before" -msgstr "変更前" +msgstr "以前" #: extras/forms/filtersets.py:490 extras/tables/tables.py:431 #: templates/extras/htmx/report_result.html:43 @@ -6448,26 +6455,25 @@ msgstr "動作" #: extras/forms/model_forms.py:62 msgid "Values" -msgstr "価値" +msgstr "値" #: extras/forms/model_forms.py:71 msgid "" "The type of data stored in this field. For object/multi-object fields, " "select the related object type below." -msgstr "" -"このフィールドに保存されているデータのタイプ。オブジェクト/マルチオブジェクトフィールドの場合は、関連するオブジェクトタイプを以下から選択してください。" +msgstr "このフィールドのタイプ。オブジェクト/マルチオブジェクトフィールドの場合は、関連するオブジェクトタイプを以下から選択してください。" #: extras/forms/model_forms.py:74 msgid "" "This will be displayed as help text for the form field. Markdown is " "supported." -msgstr "これはフォームフィールドのヘルプテキストとして表示されます。Markdown はサポートされています。" +msgstr "これはフォームフィールドのヘルプテキストとして表示されます。Markdown がサポートされています。" #: extras/forms/model_forms.py:91 msgid "" "Enter one choice per line. An optional label may be specified for each " "choice by appending it with a colon. Example:" -msgstr "1 行に 1 つの選択肢を入力します。各選択肢にコロンを付けることでオプションのラベルを指定できます。例:" +msgstr "1 行に 1 つの選択肢を入力します。必要に応じて、各選択肢にコロンを付けることで、ラベルを指定できます。例:" #: extras/forms/model_forms.py:132 templates/extras/customlink.html:10 msgid "Custom Link" @@ -6506,7 +6512,7 @@ msgstr "レンダリング" #: extras/forms/model_forms.py:182 extras/forms/model_forms.py:534 msgid "Template content is populated from the remote source selected below." -msgstr "テンプレートコンテンツは、以下で選択したリモートソースから入力されます。" +msgstr "選択したリモートソースから、テンプレートコンテンツが入力されます。" #: extras/forms/model_forms.py:189 extras/forms/model_forms.py:541 msgid "Must specify either local content or a data file" @@ -6527,17 +6533,17 @@ msgstr "SSL" #: extras/forms/model_forms.py:257 msgid "Action choice" -msgstr "アクション選択" +msgstr "スクリプト" #: extras/forms/model_forms.py:262 msgid "Enter conditions in JSON format." -msgstr "に条件を入力 JSON フォーマット。" +msgstr "JSON フォーマットで条件を入力。" #: extras/forms/model_forms.py:266 msgid "" "Enter parameters to pass to the action in JSON format." -msgstr "アクションに渡すパラメータを入力してください JSON フォーマット。" +msgstr "JSON フォーマットでアクションに渡すパラメータを入力してください。" #: extras/forms/model_forms.py:270 templates/extras/eventrule.html:11 msgid "Event Rule" @@ -6549,11 +6555,11 @@ msgstr "条件" #: extras/forms/model_forms.py:286 msgid "Creations" -msgstr "クリエーション" +msgstr "作成" #: extras/forms/model_forms.py:287 msgid "Updates" -msgstr "アップデート" +msgstr "更新" #: extras/forms/model_forms.py:288 msgid "Deletions" @@ -6577,7 +6583,7 @@ msgstr "テナント" #: templates/ipam/ipaddress.html:62 templates/ipam/vlan_edit.html:30 #: tenancy/forms/filtersets.py:86 users/forms/model_forms.py:324 msgid "Assignment" -msgstr "アサイメント" +msgstr "割当" #: extras/forms/model_forms.py:491 msgid "Data is populated from the remote source selected below." @@ -6589,28 +6595,28 @@ msgstr "ローカルデータまたはデータファイルのいずれかを指 #: extras/forms/model_forms.py:516 templates/core/datafile.html:65 msgid "Content" -msgstr "[コンテンツ]" +msgstr "コンテンツ" #: extras/forms/reports.py:18 extras/forms/scripts.py:24 msgid "Schedule at" -msgstr "のスケジュール" +msgstr "スケジュール" #: extras/forms/reports.py:19 msgid "Schedule execution of report to a set time" -msgstr "レポートの実行を設定された時間にスケジュールする" +msgstr "レポートの実行をスケジュールする" #: extras/forms/reports.py:24 extras/forms/scripts.py:30 msgid "Recurs every" -msgstr "毎回繰り返す" +msgstr "繰り返す" #: extras/forms/reports.py:28 msgid "Interval at which this report is re-run (in minutes)" -msgstr "このレポートが再実行される間隔 (分単位)" +msgstr "実行される間隔 (分)" #: extras/forms/reports.py:36 extras/forms/scripts.py:42 #, python-brace-format msgid " (current time: {now})" -msgstr " (現在の時刻: {now})" +msgstr " (現在時刻: {now})" #: extras/forms/reports.py:46 extras/forms/scripts.py:52 msgid "Scheduled time must be in the future." @@ -6622,23 +6628,23 @@ msgstr "変更をコミット" #: extras/forms/scripts.py:19 msgid "Commit changes to the database (uncheck for a dry-run)" -msgstr "変更をデータベースにコミットする (ドライランの場合はチェックを外す)" +msgstr "変更をDBにコミットする (dry runの場合はチェックを外す)" #: extras/forms/scripts.py:25 msgid "Schedule execution of script to a set time" -msgstr "設定した時間にスクリプトの実行をスケジュールする" +msgstr "スクリプトの実行をスケジュールする" #: extras/forms/scripts.py:34 msgid "Interval at which this script is re-run (in minutes)" -msgstr "このスクリプトが再実行される間隔 (分単位)" +msgstr "実行される間隔 (分単位)" #: extras/management/commands/reindex.py:66 msgid "No indexers found!" -msgstr "インデクサーが見つかりません!" +msgstr "indexerが見つかりません" #: extras/models/change_logging.py:24 msgid "time" -msgstr "時間" +msgstr "時刻" #: extras/models/change_logging.py:37 msgid "user name" @@ -6658,7 +6664,7 @@ msgstr "変更前データ" #: extras/models/change_logging.py:87 msgid "post-change data" -msgstr "変更後のデータ" +msgstr "変更後データ" #: extras/models/change_logging.py:101 msgid "object change" @@ -6666,12 +6672,12 @@ msgstr "オブジェクト変更" #: extras/models/change_logging.py:102 msgid "object changes" -msgstr "オブジェクトの変更" +msgstr "オブジェクト変更" #: extras/models/change_logging.py:118 #, python-brace-format msgid "Change logging is not supported for this object type ({type})." -msgstr "このオブジェクトタイプでは変更ログはサポートされていません ({type})。" +msgstr "このオブジェクトタイプ ({type}) では変更ログはサポートされていません。" #: extras/models/configs.py:130 msgid "config context" @@ -6679,7 +6685,7 @@ msgstr "コンフィグコンテキスト" #: extras/models/configs.py:131 msgid "config contexts" -msgstr "設定コンテキスト" +msgstr "コンフィグコンテキスト" #: extras/models/configs.py:149 extras/models/configs.py:205 msgid "JSON data must be in object form. Example:" @@ -6689,7 +6695,7 @@ msgstr "JSON データはオブジェクト形式である必要があります msgid "" "Local config context data takes precedence over source contexts in the final" " rendered config context" -msgstr "ローカル構成コンテキストデータは、最終的にレンダリングされた構成コンテキストのソースコンテキストよりも優先されます" +msgstr "最終的なコンフィグコンテキストでは、ローカルコンフィグコンテキストが優先されます。" #: extras/models/configs.py:224 msgid "template code" @@ -6709,9 +6715,9 @@ msgid "" "href=\"https://jinja.palletsprojects.com/en/3.1.x/api/#jinja2.Environment\">additional" " parameters to pass when constructing the Jinja2 environment." msgstr "" -"任意 追加パラメータ" -" Jinja2 環境を構築するときに渡されます。" +" はJinja2 環境を構築するときに渡されます。" #: extras/models/configs.py:240 msgid "config template" @@ -6739,7 +6745,7 @@ msgstr "内部フィールド名" #: extras/models/customfields.py:96 msgid "Only alphanumeric characters and underscores are allowed." -msgstr "英数字とアンダースコアのみを使用できます。" +msgstr "英数字とアンダースコアのみ使用できます。" #: extras/models/customfields.py:101 msgid "Double underscores are not permitted in custom field names." @@ -6749,7 +6755,7 @@ msgstr "カスタムフィールド名には二重アンダースコアを使用 msgid "" "Name of the field as displayed to users (if not provided, 'the field's name " "will be used)" -msgstr "ユーザに表示されるフィールドの名前 (指定しない場合は、「フィールドの名前が使用されます)」" +msgstr "表示されるフィールド名 (指定しない場合は、フィールド名が使用されます)" #: extras/models/customfields.py:116 extras/models/models.py:347 msgid "group name" @@ -6767,17 +6773,17 @@ msgstr "必須" msgid "" "If true, this field is required when creating new objects or editing an " "existing object." -msgstr "true の場合、新しいオブジェクトを作成したり、既存のオブジェクトを編集したりするときに、このフィールドは必須です。" +msgstr "true の場合、オブジェクトを作成・編集する際に、このフィールドは必須です。" #: extras/models/customfields.py:132 msgid "search weight" -msgstr "検索ウェイト" +msgstr "検索優先度" #: extras/models/customfields.py:135 msgid "" "Weighting for search. Lower values are considered more important. Fields " "with a search weight of zero will be ignored." -msgstr "検索用の重み付け。値が小さいほど重要であると見なされます。検索ウェイトが 0 のフィールドは無視されます。" +msgstr "検索用の重み付け。値が小さいほど優先されます。検索優先度が 0 のフィールドは無視されます。" #: extras/models/customfields.py:140 msgid "filter logic" @@ -6787,7 +6793,7 @@ msgstr "フィルターロジック" msgid "" "Loose matches any instance of a given string; exact matches the entire " "field." -msgstr "Loose は指定した文字列の任意のインスタンスと一致し、exact はフィールド全体と一致します。" +msgstr "Loose は指定した文字列が含まれる場合に一致し、exact はフィールド全体と一致します。" #: extras/models/customfields.py:147 msgid "default" @@ -6801,11 +6807,11 @@ msgstr "フィールドのデフォルト値 (JSON 値である必要があり #: extras/models/customfields.py:156 msgid "display weight" -msgstr "ディスプレイ重量" +msgstr "表示優先度" #: extras/models/customfields.py:157 msgid "Fields with higher weights appear lower in a form." -msgstr "重みが大きいフィールドは、フォームの下位に表示されます。" +msgstr "値が大きいフィールドは、フォームの下に表示されます。" #: extras/models/customfields.py:162 msgid "minimum value" @@ -6825,7 +6831,7 @@ msgstr "最大許容値 (数値フィールド用)" #: extras/models/customfields.py:175 msgid "validation regex" -msgstr "検証正規表現" +msgstr "バリデーション正規表現" #: extras/models/customfields.py:177 #, python-brace-format @@ -6835,11 +6841,11 @@ msgid "" "values to exactly three uppercase letters." msgstr "" "テキストフィールド値に適用する正規表現。^ と $ を使用して文字列全体を強制的に一致させます。例えば、 ^ " -"[アルファベット順]{3}$ 値をちょうど 3 文字の大文字に制限します。" +"[A-Z]{3}$ は値を3 字の大文字に制限します。" #: extras/models/customfields.py:185 msgid "choice set" -msgstr "チョイスセット" +msgstr "選択肢" #: extras/models/customfields.py:194 msgid "Specifies whether the custom field is displayed in the UI" @@ -6851,11 +6857,11 @@ msgstr "カスタムフィールド値を UI で編集できるかどうかを #: extras/models/customfields.py:205 msgid "is cloneable" -msgstr "クローン可能" +msgstr "複製可能" #: extras/models/customfields.py:206 msgid "Replicate this value when cloning objects" -msgstr "オブジェクトのクローニング時にこの値を複製する" +msgstr "オブジェクトの複製時にこの値を複製する" #: extras/models/customfields.py:219 msgid "custom field" @@ -6868,7 +6874,7 @@ msgstr "カスタムフィールド" #: extras/models/customfields.py:309 #, python-brace-format msgid "Invalid default value \"{value}\": {error}" -msgstr "デフォルト値が無効です」{value}「: {error}" +msgstr "デフォルト値が無効です \"{value}\": {error}" #: extras/models/customfields.py:316 msgid "A minimum value may be set only for numeric fields" @@ -6902,7 +6908,7 @@ msgstr "{type} フィールドはオブジェクトタイプを定義できま #: extras/models/customfields.py:434 msgid "True" -msgstr "本当" +msgstr "真" #: extras/models/customfields.py:435 msgid "False" @@ -6911,7 +6917,7 @@ msgstr "偽" #: extras/models/customfields.py:517 #, python-brace-format msgid "Values must match this regex: {regex}" -msgstr "値は次の正規表現と一致する必要があります。 {regex}" +msgstr "値は次の正規表現とマッチする必要があります。 {regex}" #: extras/models/customfields.py:611 msgid "Value must be a string." @@ -6920,7 +6926,7 @@ msgstr "値は文字列でなければなりません。" #: extras/models/customfields.py:613 #, python-brace-format msgid "Value must match regex '{regex}'" -msgstr "値は正規表現 'と一致する必要があります{regex}'" +msgstr "値は正規表現 '{regex}'と一致する必要があります" #: extras/models/customfields.py:618 msgid "Value must be an integer." @@ -6929,16 +6935,16 @@ msgstr "値は整数でなければなりません。" #: extras/models/customfields.py:621 extras/models/customfields.py:636 #, python-brace-format msgid "Value must be at least {minimum}" -msgstr "値は少なくとも次の値でなければなりません {minimum}" +msgstr "値は {minimum} 以上でなければなりません" #: extras/models/customfields.py:625 extras/models/customfields.py:640 #, python-brace-format msgid "Value must not exceed {maximum}" -msgstr "値を超えてはいけません {maximum}" +msgstr "値は {maximum} を超えてはいけません" #: extras/models/customfields.py:633 msgid "Value must be a decimal." -msgstr "値は10進数でなければなりません。" +msgstr "値は実数でなければなりません。" #: extras/models/customfields.py:645 msgid "Value must be true or false." @@ -6950,27 +6956,27 @@ msgstr "日付値は ISO 8601 フォーマット (YYYY-MM-DD) である必要が #: extras/models/customfields.py:662 msgid "Date and time values must be in ISO 8601 format (YYYY-MM-DD HH:MM:SS)." -msgstr "日付と時刻の値は ISO 8601 フォーマット (YYYY-MM-DD HH: MM: SS) である必要があります。" +msgstr "日付と時刻の値は ISO 8601 フォーマット (YYYY-MM-DD HH:MM:SS) である必要があります。" #: extras/models/customfields.py:669 #, python-brace-format msgid "Invalid choice ({value}) for choice set {choiceset}." -msgstr "選択が無効です ({value}) チョイスセット用 {choiceset}。" +msgstr "{value}は選択肢 {choiceset} に含まれていません。" #: extras/models/customfields.py:679 #, python-brace-format msgid "Invalid choice(s) ({value}) for choice set {choiceset}." -msgstr "選択が無効です ({value}) チョイスセット用 {choiceset}。" +msgstr "{value}は選択肢 {choiceset} に含まれていません。" #: extras/models/customfields.py:688 #, python-brace-format msgid "Value must be an object ID, not {type}" -msgstr "値はオブジェクトIDでなければならず、そうではありません {type}" +msgstr "{type}ではなく、オブジェクトIDを指定してください" #: extras/models/customfields.py:694 #, python-brace-format msgid "Value must be a list of object IDs, not {type}" -msgstr "値はオブジェクト ID のリストでなければならず、そうではありません {type}" +msgstr "{type} ではなくオブジェクト ID のリストを入力してください" #: extras/models/customfields.py:698 #, python-brace-format @@ -6991,11 +6997,11 @@ msgstr "選択肢は自動的にアルファベット順に並べられます" #: extras/models/customfields.py:739 msgid "custom field choice set" -msgstr "カスタムフィールド選択セット" +msgstr "カスタムフィールド選択肢" #: extras/models/customfields.py:740 msgid "custom field choice sets" -msgstr "カスタムフィールド選択セット" +msgstr "カスタムフィールド選択肢" #: extras/models/customfields.py:776 msgid "Must define base or extra choices." @@ -7007,7 +7013,7 @@ msgstr "レイアウト" #: extras/models/dashboard.py:23 msgid "config" -msgstr "設定する" +msgstr "設定" #: extras/models/dashboard.py:28 msgid "dashboard" @@ -7094,7 +7100,7 @@ msgstr "イベントルール" msgid "" "At least one event type must be selected: create, update, delete, job start," " and/or job end." -msgstr "少なくとも 1 つのイベントタイプ (作成、更新、削除、ジョブの開始、ジョブの終了) を選択する必要があります。" +msgstr "少なくとも 1 つのイベントタイプを選択する必要があります。 : 作成、更新、削除、ジョブの開始、ジョブの終了" #: extras/models/models.py:196 msgid "" @@ -7140,13 +7146,13 @@ msgid "" "username, request_id, and data." msgstr "" "カスタムリクエストボディ用の Jinja2 テンプレート。空欄の場合は、変更を表す JSON " -"オブジェクトが含まれます。利用可能なコンテキストデータには以下が含まれます。 出来事、 " -"タイムスタンプユーザ名リクエスト ID、および " -"データ。" +"オブジェクトが含まれます。利用可能なコンテキストデータには以下が含まれます。 event, " +"model, timestamp, username, " +"request_id, and data." #: extras/models/models.py:234 msgid "secret" -msgstr "秘密" +msgstr "シークレット" #: extras/models/models.py:238 msgid "" @@ -7154,8 +7160,8 @@ msgid "" "header containing a HMAC hex digest of the payload body using the secret as " "the key. The secret is not transmitted in the request." msgstr "" -"提供された場合、リクエストには以下が含まれます X フック-シグネチャー シークレットをキーとして使用したペイロード本体の " -"HMAC 16 進ダイジェストを含むヘッダー。シークレットはリクエストでは送信されません。" +"提供された場合、リクエストにはシークレットをキーとして使用したペイロード本体のHMAC 16 進ダイジェストを含むX-Hook-" +"Signature ヘッダー が含まれます 。シークレットはリクエストでは送信されません。" #: extras/models/models.py:245 msgid "Enable SSL certificate verification. Disable with caution!" @@ -7207,15 +7213,6 @@ msgstr "リンク URL の Jinja2 テンプレートコード" msgid "Links with the same group will appear as a dropdown menu" msgstr "同じグループのリンクはドロップダウンメニューとして表示されます" -#: extras/models/models.py:353 -msgid "button class" -msgstr "ボタンクラス" - -#: extras/models/models.py:357 -msgid "" -"The class of the first link in a group will be used for the dropdown button" -msgstr "グループ内の最初のリンクのクラスがドロップダウンボタンに使用されます" - #: extras/models/models.py:360 msgid "new window" msgstr "新しいウィンドウ" @@ -7241,12 +7238,12 @@ msgid "" "Jinja2 template code. The list of objects being exported is passed as a " "context variable named queryset." msgstr "" -"Jinja2 テンプレートコード。エクスポートされるオブジェクトのリストは、という名前のコンテキスト変数として渡されます。 " -"クエリーセット。" +"Jinja2 テンプレートコード。エクスポートされるオブジェクトのリストは、 " +"クエリーセットという名前のコンテキスト変数として渡されます。" #: extras/models/models.py:440 msgid "Defaults to text/plain; charset=utf-8" -msgstr "デフォルトは テキスト/プレーン; 文字セット=utf-8" +msgstr "デフォルトは text/plain; charset=utf-8" #: extras/models/models.py:443 msgid "file extension" @@ -7262,20 +7259,20 @@ msgstr "添付ファイルとして" #: extras/models/models.py:451 msgid "Download file as attachment" -msgstr "ファイルを添付ファイルとしてダウンロード" +msgstr "ファイルを直接ダウンロードする" #: extras/models/models.py:460 msgid "export template" -msgstr "テンプレートをエクスポート" +msgstr "エクスポートテンプレート" #: extras/models/models.py:461 msgid "export templates" -msgstr "テンプレートをエクスポートする" +msgstr "エクスポートテンプレート" #: extras/models/models.py:478 #, python-brace-format msgid "\"{name}\" is a reserved name. Please choose a different name." -msgstr "「{name}「は予約名です。別の名前を選択してください。" +msgstr "\"{name}\"は予約されています。別の名前を選択してください。" #: extras/models/models.py:528 msgid "The object type(s) to which this filter applies." @@ -7287,19 +7284,19 @@ msgstr "共有した" #: extras/models/models.py:573 msgid "saved filter" -msgstr "保存済みフィルター" +msgstr "保存済みフィルタ" #: extras/models/models.py:574 msgid "saved filters" -msgstr "保存済みフィルター" +msgstr "保存済みフィルタ" #: extras/models/models.py:592 msgid "Filter parameters must be stored as a dictionary of keyword arguments." -msgstr "フィルターパラメータは、キーワード引数の辞書として保存する必要があります。" +msgstr "フィルタパラメータは、キーワード引数の辞書として保存する必要があります。" #: extras/models/models.py:620 msgid "image height" -msgstr "画像の高さ" +msgstr "画像高さ" #: extras/models/models.py:623 msgid "image width" @@ -7307,16 +7304,16 @@ msgstr "画像幅" #: extras/models/models.py:640 msgid "image attachment" -msgstr "画像添付" +msgstr "添付画像" #: extras/models/models.py:641 msgid "image attachments" -msgstr "画像添付ファイル" +msgstr "添付画像" #: extras/models/models.py:655 #, python-brace-format msgid "Image attachments cannot be assigned to this object type ({type})." -msgstr "このオブジェクトタイプにはイメージ添付ファイルを割り当てることができません ({type})。" +msgstr "このオブジェクトタイプ ({type})には添付画像を割り当てることができません。" #: extras/models/models.py:718 msgid "kind" @@ -7333,7 +7330,7 @@ msgstr "ジャーナルエントリ" #: extras/models/models.py:748 #, python-brace-format msgid "Journaling is not supported for this object type ({type})." -msgstr "このオブジェクトタイプではジャーナリングはサポートされていません ({type})。" +msgstr "このオブジェクトタイプ({type})ではジャーナリングはサポートされていません 。" #: extras/models/models.py:790 msgid "bookmark" @@ -7346,7 +7343,7 @@ msgstr "ブックマーク" #: extras/models/models.py:804 #, python-brace-format msgid "Bookmarks cannot be assigned to this object type ({type})." -msgstr "このオブジェクトタイプにはブックマークを割り当てられません ({type})。" +msgstr "このオブジェクトタイプ ({type})にはブックマークを割り当てられません。" #: extras/models/reports.py:46 msgid "report module" @@ -7382,7 +7379,7 @@ msgstr "キャッシュ値" #: extras/models/search.py:59 msgid "cached values" -msgstr "キャッシュされた値" +msgstr "キャッシュ値" #: extras/models/staging.py:44 msgid "branch" @@ -7390,7 +7387,7 @@ msgstr "ブランチ" #: extras/models/staging.py:45 msgid "branches" -msgstr "枝" +msgstr "ブランチ" #: extras/models/staging.py:97 msgid "staged change" @@ -7398,15 +7395,15 @@ msgstr "段階的変更" #: extras/models/staging.py:98 msgid "staged changes" -msgstr "段階的な変更" +msgstr "段階的変更" #: extras/models/tags.py:40 -msgid "The object type(s) to which this this tag can be applied." +msgid "The object type(s) to which this tag can be applied." msgstr "このタグを適用できるオブジェクトタイプ。" #: extras/models/tags.py:49 msgid "tag" -msgstr "鬼ごっこ" +msgstr "タグ" #: extras/models/tags.py:50 msgid "tags" @@ -7430,7 +7427,7 @@ msgstr "スクリプトデータ" #: extras/scripts.py:375 msgid "Script Execution Parameters" -msgstr "スクリプト実行パラメーター" +msgstr "スクリプト実行パラメータ" #: extras/signals.py:121 #, python-brace-format @@ -7441,7 +7438,7 @@ msgstr "削除は保護ルールによって禁止されています。 {message #: extras/tables/tables.py:143 extras/tables/tables.py:208 #: extras/tables/tables.py:285 msgid "Content Types" -msgstr "コンテンツタイプ" +msgstr "コンテントタイプ" #: extras/tables/tables.py:50 msgid "Visible" @@ -7457,7 +7454,7 @@ msgstr "チョイスセット" #: extras/tables/tables.py:68 msgid "Is Cloneable" -msgstr "クローニング可能" +msgstr "複製可能" #: extras/tables/tables.py:98 msgid "Count" @@ -7469,7 +7466,7 @@ msgstr "アルファベット順に並べる" #: extras/tables/tables.py:125 templates/extras/customlink.html:34 msgid "New Window" -msgstr "[新規ウィンドウ]" +msgstr "新規ウィンドウ" #: extras/tables/tables.py:146 msgid "As Attachment" @@ -7493,11 +7490,11 @@ msgstr "同期済み" #: extras/tables/tables.py:178 msgid "Content Type" -msgstr "コンテンツタイプ" +msgstr "コンテントタイプ" #: extras/tables/tables.py:185 msgid "Image" -msgstr "[イメージ]" +msgstr "画像" #: extras/tables/tables.py:190 msgid "Size (Bytes)" @@ -7537,12 +7534,12 @@ msgstr "コメント (ショート)" #: extras/validators.py:15 #, python-format msgid "Ensure this value is equal to %(limit_value)s." -msgstr "この値が次の値と等しいことを確認してください %(limit_value)s。" +msgstr "%(limit_value)sと等しいことを確認する 。" #: extras/validators.py:26 #, python-format msgid "Ensure this value does not equal %(limit_value)s." -msgstr "この値が等しくないことを確認してください %(limit_value)s。" +msgstr "%(limit_value)sと等しくないことを確認する。" #: extras/validators.py:37 msgid "This field must be empty." @@ -7555,7 +7552,7 @@ msgstr "このフィールドは空であってはなりません。" #: extras/validators.py:121 #, python-brace-format msgid "Invalid attribute \"{name}\" for {model}" -msgstr "属性が無効です」{name}「用 {model}" +msgstr "{model}において{name}属性は無効です" #: extras/views.py:880 msgid "Your dashboard has been reset." @@ -7563,7 +7560,7 @@ msgstr "ダッシュボードがリセットされました。" #: ipam/api/field_serializers.py:17 msgid "Enter a valid IPv4 or IPv6 address with optional mask." -msgstr "オプションのマスクを使用して有効な IPv4 または IPv6 アドレスを入力します。" +msgstr "有効な IPv4 または IPv6 アドレスを入力してください。(サブネットマスクが使用可能)" #: ipam/api/field_serializers.py:24 #, python-brace-format @@ -7594,7 +7591,7 @@ msgstr "DHCP" #: ipam/choices.py:73 msgid "SLAAC" -msgstr "スラーク" +msgstr "SLAAC" #: ipam/choices.py:89 msgid "Loopback" @@ -7668,7 +7665,7 @@ msgstr "プレフィックス" #: ipam/filtersets.py:136 ipam/filtersets.py:175 ipam/filtersets.py:198 msgid "RIR (ID)" -msgstr "リル (アイディー)" +msgstr "RIR (ID)" #: ipam/filtersets.py:142 ipam/filtersets.py:181 ipam/filtersets.py:204 msgid "RIR (slug)" @@ -7700,7 +7697,7 @@ msgid "VLAN number (1-4094)" msgstr "VLAN 番号 (1-4094)" #: ipam/filtersets.py:437 ipam/filtersets.py:441 ipam/filtersets.py:533 -#: ipam/forms/model_forms.py:444 templates/tenancy/contact.html:54 +#: ipam/forms/model_forms.py:430 templates/tenancy/contact.html:54 #: tenancy/forms/bulk_edit.py:112 msgid "Address" msgstr "住所" @@ -7817,7 +7814,7 @@ msgstr "非公開です" #: templates/ipam/aggregate.html:19 templates/ipam/asn.html:28 #: templates/ipam/asnrange.html:20 templates/ipam/rir.html:20 msgid "RIR" -msgstr "リル" +msgstr "RIR" #: ipam/forms/bulk_edit.py:168 msgid "Date added" @@ -7869,7 +7866,7 @@ msgid "Authentication key" msgstr "認証キー" #: ipam/forms/bulk_edit.py:404 ipam/forms/filtersets.py:369 -#: ipam/forms/model_forms.py:455 netbox/navigation/menu.py:376 +#: ipam/forms/model_forms.py:441 netbox/navigation/menu.py:376 #: templates/ipam/fhrpgroup.html:51 #: templates/wireless/inc/authentication_attrs.html:5 #: wireless/forms/bulk_edit.py:90 wireless/forms/bulk_edit.py:137 @@ -7886,11 +7883,11 @@ msgstr "子 VLAN VID の最小値" msgid "Maximum child VLAN VID" msgstr "子 VLAN VID の最大数" -#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:527 +#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:531 msgid "Scope type" msgstr "スコープタイプ" -#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:600 +#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:604 #: ipam/tables/vlans.py:71 templates/ipam/vlangroup.html:39 msgid "Scope" msgstr "スコープ" @@ -7899,8 +7896,8 @@ msgstr "スコープ" msgid "Site & Group" msgstr "サイトとグループ" -#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:663 -#: ipam/forms/model_forms.py:697 ipam/tables/services.py:19 +#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:667 +#: ipam/forms/model_forms.py:701 ipam/tables/services.py:19 #: ipam/tables/services.py:49 templates/ipam/service.html:39 #: templates/ipam/servicetemplate.html:24 msgid "Ports" @@ -7940,7 +7937,7 @@ msgid "Parent device of assigned interface (if any)" msgstr "割当インタフェースの親デバイス (存在する場合)" #: ipam/forms/bulk_import.py:310 ipam/forms/bulk_import.py:496 -#: ipam/forms/model_forms.py:691 virtualization/filtersets.py:284 +#: ipam/forms/model_forms.py:695 virtualization/filtersets.py:284 #: virtualization/filtersets.py:323 virtualization/forms/bulk_edit.py:199 #: virtualization/forms/bulk_edit.py:325 #: virtualization/forms/bulk_import.py:146 @@ -8115,8 +8112,8 @@ msgstr "ポート" #: virtualization/forms/filtersets.py:189 #: virtualization/forms/filtersets.py:234 #: virtualization/forms/model_forms.py:223 -#: virtualization/tables/virtualmachines.py:115 -#: virtualization/tables/virtualmachines.py:168 vpn/choices.py:45 +#: virtualization/tables/virtualmachines.py:128 +#: virtualization/tables/virtualmachines.py:181 vpn/choices.py:45 #: vpn/forms/filtersets.py:289 vpn/forms/model_forms.py:161 #: vpn/forms/model_forms.py:172 vpn/forms/model_forms.py:274 msgid "Virtual Machine" @@ -8139,7 +8136,7 @@ msgstr "サイト/VLAN 割り当て" msgid "IP Range" msgstr "IP アドレス範囲" -#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:454 +#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:440 #: templates/ipam/fhrpgroup.html:19 templates/ipam/ipaddress_edit.html:52 msgid "FHRP Group" msgstr "FHRP グループ" @@ -8152,7 +8149,7 @@ msgstr "これをデバイス/仮想マシンのプライマリIPにする" msgid "An IP address can only be assigned to a single object." msgstr "IP アドレスは 1 つのオブジェクトにのみ割り当てることができます。" -#: ipam/forms/model_forms.py:357 ipam/models/ip.py:877 +#: ipam/forms/model_forms.py:357 ipam/models/ip.py:896 msgid "" "Cannot reassign IP address while it is designated as the primary IP for the " "parent object" @@ -8163,46 +8160,39 @@ msgid "" "Only IP addresses assigned to an interface can be designated as primary IPs." msgstr "プライマリ IP として指定できるのは、インタフェースに割当 IP アドレスのみです。" -#: ipam/forms/model_forms.py:373 -#, python-brace-format -msgid "{ip} is a network ID, which may not be assigned to an interface." -msgstr "{ip} はネットワーク ID で、インタフェースに割り当てることはできません。" - -#: ipam/forms/model_forms.py:379 -#, python-brace-format -msgid "" -"{ip} is a broadcast address, which may not be assigned to an interface." -msgstr "{ip} はブロードキャストアドレスで、インタフェースに割り当てることはできません。" - -#: ipam/forms/model_forms.py:456 +#: ipam/forms/model_forms.py:442 msgid "Virtual IP Address" msgstr "仮想 IP アドレス" -#: ipam/forms/model_forms.py:598 ipam/forms/model_forms.py:637 +#: ipam/forms/model_forms.py:523 +msgid "Assignment already exists" +msgstr "アサインメントは既に存在します" + +#: ipam/forms/model_forms.py:602 ipam/forms/model_forms.py:641 #: ipam/tables/ip.py:250 templates/ipam/vlan_edit.html:37 #: templates/ipam/vlangroup.html:27 msgid "VLAN Group" msgstr "VLAN グループ" -#: ipam/forms/model_forms.py:599 +#: ipam/forms/model_forms.py:603 msgid "Child VLANs" msgstr "子 VLAN" -#: ipam/forms/model_forms.py:668 ipam/forms/model_forms.py:702 +#: ipam/forms/model_forms.py:672 ipam/forms/model_forms.py:706 msgid "" "Comma-separated list of one or more port numbers. A range may be specified " "using a hyphen." msgstr "1 つ以上のポート番号をカンマで区切ったリスト。範囲はハイフンを使用して指定できます。" -#: ipam/forms/model_forms.py:673 templates/ipam/servicetemplate.html:12 +#: ipam/forms/model_forms.py:677 templates/ipam/servicetemplate.html:12 msgid "Service Template" msgstr "サービステンプレート" -#: ipam/forms/model_forms.py:724 +#: ipam/forms/model_forms.py:728 msgid "Service template" msgstr "サービステンプレート" -#: ipam/forms/model_forms.py:754 +#: ipam/forms/model_forms.py:758 msgid "" "Must specify name, protocol, and port(s) if not using a service template." msgstr "サービステンプレートを使用しない場合は、名前、プロトコル、およびポートを指定する必要があります。" @@ -8362,12 +8352,12 @@ msgstr "プレフィックス" msgid "Cannot create prefix with /0 mask." msgstr "/0 マスクではプレフィックスを作成できません。" -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 #, python-brace-format msgid "VRF {vrf}" msgstr "VRF {vrf}" -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 msgid "global table" msgstr "グローバルテーブル" @@ -8460,12 +8450,23 @@ msgstr "IP アドレス" msgid "Cannot create IP address with /0 mask." msgstr "/0 マスクで IP アドレスを作成することはできません。" -#: ipam/models/ip.py:856 +#: ipam/models/ip.py:850 +#, python-brace-format +msgid "{ip} is a network ID, which may not be assigned to an interface." +msgstr "{ip} はネットワーク ID で、インタフェースに割り当てることはできません。" + +#: ipam/models/ip.py:861 +#, python-brace-format +msgid "" +"{ip} is a broadcast address, which may not be assigned to an interface." +msgstr "{ip} はブロードキャストアドレスで、インタフェースに割り当てることはできません。" + +#: ipam/models/ip.py:875 #, python-brace-format msgid "Duplicate IP address found in {table}: {ipaddress}" msgstr "重複した IP アドレスが見つかりました {table}: {ipaddress}" -#: ipam/models/ip.py:883 +#: ipam/models/ip.py:902 msgid "Only IPv6 addresses can be assigned SLAAC status" msgstr "SLAAC ステータスを割り当てることができるのは IPv6 アドレスのみです" @@ -9224,7 +9225,7 @@ msgstr "[仮想マシン]" #: templates/virtualization/virtualmachine.html:177 #: templates/virtualization/virtualmachine/base.html:32 #: templates/virtualization/virtualmachine_list.html:21 -#: virtualization/tables/virtualmachines.py:90 virtualization/views.py:389 +#: virtualization/tables/virtualmachines.py:103 virtualization/views.py:389 msgid "Virtual Disks" msgstr "仮想ディスク" @@ -10140,8 +10141,8 @@ msgstr "スケジューリング" #: templates/core/job.html:66 #, python-format -msgid "every %(interval)s seconds" -msgstr "ごと %(interval)s 秒" +msgid "every %(interval)s minutes" +msgstr "ごと %(interval)s 分" #: templates/dcim/bulk_disconnect.html:9 #, python-format @@ -12651,65 +12652,65 @@ msgstr "このオブジェクトタイプでは制約はサポートされてい msgid "Invalid filter for {model}: {error}" msgstr "のフィルタが無効です {model}: {error}" -#: users/models.py:54 +#: users/models.py:55 msgid "user" msgstr "ユーザ" -#: users/models.py:55 +#: users/models.py:56 msgid "users" msgstr "ユーザ" -#: users/models.py:66 +#: users/models.py:67 msgid "A user with this username already exists." msgstr "このユーザ名のユーザはすでに存在します。" -#: users/models.py:78 vpn/models/crypto.py:42 +#: users/models.py:79 vpn/models/crypto.py:42 msgid "group" msgstr "グループ" -#: users/models.py:79 +#: users/models.py:80 msgid "groups" msgstr "グループ" -#: users/models.py:106 users/models.py:107 +#: users/models.py:107 users/models.py:108 msgid "user preferences" msgstr "ユーザプリファレンス" -#: users/models.py:174 +#: users/models.py:175 #, python-brace-format msgid "Key '{path}' is a leaf node; cannot assign new keys" msgstr "キー '{path}'はリーフノードです。新しいキーを割り当てることはできません" -#: users/models.py:186 +#: users/models.py:187 #, python-brace-format msgid "Key '{path}' is a dictionary; cannot assign a non-dictionary value" msgstr "キー '{path}'はディクショナリです。ディクショナリ以外の値は割り当てられません" -#: users/models.py:252 +#: users/models.py:253 msgid "expires" msgstr "期限切れ" -#: users/models.py:257 +#: users/models.py:258 msgid "last used" msgstr "最終使用日" -#: users/models.py:262 +#: users/models.py:263 msgid "key" msgstr "キー" -#: users/models.py:268 +#: users/models.py:269 msgid "write enabled" msgstr "書き込み有効" -#: users/models.py:270 +#: users/models.py:271 msgid "Permit create/update/delete operations using this key" msgstr "このキーを使用して作成/更新/削除操作を許可する" -#: users/models.py:281 +#: users/models.py:282 msgid "allowed IPs" msgstr "許可された IP" -#: users/models.py:283 +#: users/models.py:284 msgid "" "Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for" " no restrictions. Ex: \"10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64\"" @@ -12718,32 +12719,32 @@ msgstr "" "ネットワーク。制限がない場合は空白のままにしてください。例:10.1.1.0/24、192.168.10.16/32、2001: DB 8:1:: " "/64\"" -#: users/models.py:291 +#: users/models.py:296 msgid "token" msgstr "トークン" -#: users/models.py:292 +#: users/models.py:297 msgid "tokens" msgstr "トークン" -#: users/models.py:373 +#: users/models.py:378 msgid "The list of actions granted by this permission" msgstr "この権限によって付与されたアクションのリスト" -#: users/models.py:378 +#: users/models.py:383 msgid "constraints" msgstr "制約" -#: users/models.py:379 +#: users/models.py:384 msgid "" "Queryset filter matching the applicable objects of the selected type(s)" msgstr "選択したタイプの該当するオブジェクトに一致するクエリーセットフィルタ" -#: users/models.py:386 +#: users/models.py:391 msgid "permission" msgstr "許可" -#: users/models.py:387 +#: users/models.py:392 msgid "permissions" msgstr "許可" @@ -13000,43 +13001,50 @@ msgid "" "the object's change log for details." msgstr "このオブジェクトは、フォームがレンダリングされてから変更されました。詳細については、オブジェクトの変更ログを参照してください。" -#: utilities/forms/utils.py:42 utilities/forms/utils.py:65 -#: utilities/forms/utils.py:77 utilities/forms/utils.py:80 +#: utilities/forms/utils.py:42 utilities/forms/utils.py:68 +#: utilities/forms/utils.py:85 utilities/forms/utils.py:87 #, python-brace-format msgid "Range \"{value}\" is invalid." msgstr "レンジ」{value}「は無効です。" -#: utilities/forms/utils.py:225 +#: utilities/forms/utils.py:74 +#, python-brace-format +msgid "" +"Invalid range: Ending value ({end}) must be greater than beginning value " +"({begin})." +msgstr "範囲が無効です:終了値 ({end}) は開始値 () より大きくなければなりません{begin})。" + +#: utilities/forms/utils.py:232 #, python-brace-format msgid "Duplicate or conflicting column header for \"{field}\"" msgstr "「」の列ヘッダーが重複しているか、重複しています{field}」" -#: utilities/forms/utils.py:231 +#: utilities/forms/utils.py:238 #, python-brace-format msgid "Duplicate or conflicting column header for \"{header}\"" msgstr "「」の列ヘッダーが重複しているか、重複しています{header}」" -#: utilities/forms/utils.py:240 +#: utilities/forms/utils.py:247 #, python-brace-format msgid "Row {row}: Expected {count_expected} columns but found {count_found}" msgstr "行 {row}: 期待 {count_expected} 列が見つかりましたが {count_found}" -#: utilities/forms/utils.py:263 +#: utilities/forms/utils.py:270 #, python-brace-format msgid "Unexpected column header \"{field}\" found." msgstr "予期しない列ヘッダー」{field}「が見つかりました。" -#: utilities/forms/utils.py:265 +#: utilities/forms/utils.py:272 #, python-brace-format msgid "Column \"{field}\" is not a related object; cannot use dots" msgstr "コラム」{field}\"は関連オブジェクトではありません。ドットは使用できません" -#: utilities/forms/utils.py:269 +#: utilities/forms/utils.py:276 #, python-brace-format msgid "Invalid related object attribute for column \"{field}\": {to_field}" msgstr "列 \"の関連オブジェクト属性が無効です{field}「: {to_field}" -#: utilities/forms/utils.py:277 +#: utilities/forms/utils.py:284 #, python-brace-format msgid "Required column header \"{header}\" not found." msgstr "必須の列ヘッダー」{header}「が見つかりません。" @@ -13648,6 +13656,11 @@ msgstr "提案" msgid "Assigned Object Type" msgstr "割当オブジェクトタイプ" +#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 +#: vpn/forms/model_forms.py:241 vpn/tables/tunnels.py:91 +msgid "Tunnel interface" +msgstr "トンネルインターフェイス" + #: vpn/forms/model_forms.py:147 msgid "First Termination" msgstr "1 回目の解約" diff --git a/netbox/translations/pt/LC_MESSAGES/django.mo b/netbox/translations/pt/LC_MESSAGES/django.mo index 04bb568977f28a30425777b14a7f8ccb5b9fc289..ad8ae9463a7dbf74f9069e239f70816f0d2865f7 100644 GIT binary patch delta 61632 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

    yTv&!G+*gTmPWoSj4M?+3@{Ad^`LyR`3CH*;N`X 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 delta 61363 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 " @@ -4771,7 +4767,7 @@ msgstr "" "href=\"{url}\">{racked_instance_count} instâncias já montado dentro de " "racks." -#: dcim/models/devices.py:330 +#: dcim/models/devices.py:331 msgid "" "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." @@ -4780,151 +4776,151 @@ msgstr "" "associados a esse dispositivo antes de desclassificá-lo como dispositivo " "principal." -#: dcim/models/devices.py:336 +#: dcim/models/devices.py:337 msgid "Child device types must be 0U." msgstr "Os tipos de dispositivos infantis devem ser 0U." -#: dcim/models/devices.py:404 +#: dcim/models/devices.py:405 msgid "module type" msgstr "tipo de módulo" -#: dcim/models/devices.py:405 +#: dcim/models/devices.py:406 msgid "module types" msgstr "tipos de módulo" -#: dcim/models/devices.py:473 +#: dcim/models/devices.py:475 msgid "Virtual machines may be assigned to this role" msgstr "Máquinas virtuais podem ser atribuídas a essa função" -#: dcim/models/devices.py:485 +#: dcim/models/devices.py:487 msgid "device role" msgstr "função do dispositivo" -#: dcim/models/devices.py:486 +#: dcim/models/devices.py:488 msgid "device roles" msgstr "funções do dispositivo" -#: dcim/models/devices.py:503 +#: dcim/models/devices.py:505 msgid "Optionally limit this platform to devices of a certain manufacturer" msgstr "" "Opcionalmente, limite essa plataforma a dispositivos de um determinado " "fabricante" -#: dcim/models/devices.py:515 +#: dcim/models/devices.py:517 msgid "platform" msgstr "plataforma" -#: dcim/models/devices.py:516 +#: dcim/models/devices.py:518 msgid "platforms" msgstr "plataformas" -#: dcim/models/devices.py:564 +#: dcim/models/devices.py:566 msgid "The function this device serves" msgstr "A função que este dispositivo serve" -#: dcim/models/devices.py:596 +#: dcim/models/devices.py:598 msgid "Chassis serial number, assigned by the manufacturer" msgstr "Número de série do chassi, atribuído pelo fabricante" -#: dcim/models/devices.py:604 dcim/models/devices.py:1181 +#: dcim/models/devices.py:606 dcim/models/devices.py:1186 msgid "A unique tag used to identify this device" msgstr "Uma tag exclusiva usada para identificar esse dispositivo" -#: dcim/models/devices.py:631 +#: dcim/models/devices.py:633 msgid "position (U)" msgstr "posição (U)" -#: dcim/models/devices.py:638 +#: dcim/models/devices.py:640 msgid "rack face" msgstr "face de cremalheira" -#: dcim/models/devices.py:658 dcim/models/devices.py:1390 +#: dcim/models/devices.py:660 dcim/models/devices.py:1395 #: virtualization/models/virtualmachines.py:98 msgid "primary IPv4" msgstr "IPv4 primário" -#: dcim/models/devices.py:666 dcim/models/devices.py:1398 +#: dcim/models/devices.py:668 dcim/models/devices.py:1403 #: virtualization/models/virtualmachines.py:106 msgid "primary IPv6" msgstr "IPv6 primário" -#: dcim/models/devices.py:674 +#: dcim/models/devices.py:676 msgid "out-of-band IP" msgstr "IP fora de banda" -#: dcim/models/devices.py:691 +#: dcim/models/devices.py:693 msgid "VC position" msgstr "Posição VC" -#: dcim/models/devices.py:695 +#: dcim/models/devices.py:697 msgid "Virtual chassis position" msgstr "Posição do chassi virtual" -#: dcim/models/devices.py:698 +#: dcim/models/devices.py:700 msgid "VC priority" msgstr "Prioridade VC" -#: dcim/models/devices.py:702 +#: dcim/models/devices.py:704 msgid "Virtual chassis master election priority" msgstr "Prioridade de eleição do mestre do chassi virtual" -#: dcim/models/devices.py:705 dcim/models/sites.py:207 +#: dcim/models/devices.py:707 dcim/models/sites.py:207 msgid "latitude" msgstr "latitude" -#: dcim/models/devices.py:710 dcim/models/devices.py:718 +#: dcim/models/devices.py:712 dcim/models/devices.py:720 #: dcim/models/sites.py:212 dcim/models/sites.py:220 msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgstr "Coordenada GPS em formato decimal (xx.yyyyyy)" -#: dcim/models/devices.py:713 dcim/models/sites.py:215 +#: dcim/models/devices.py:715 dcim/models/sites.py:215 msgid "longitude" msgstr "longitude" -#: dcim/models/devices.py:786 +#: dcim/models/devices.py:788 msgid "Device name must be unique per site." msgstr "O nome do dispositivo deve ser exclusivo por site." -#: dcim/models/devices.py:797 ipam/models/services.py:75 +#: dcim/models/devices.py:799 ipam/models/services.py:75 msgid "device" msgstr "dispositivo" -#: dcim/models/devices.py:798 +#: dcim/models/devices.py:800 msgid "devices" msgstr "dispositivos" -#: dcim/models/devices.py:838 +#: dcim/models/devices.py:840 #, python-brace-format msgid "Rack {rack} does not belong to site {site}." msgstr "Rack {rack} não pertence ao site {site}." -#: dcim/models/devices.py:843 +#: dcim/models/devices.py:845 #, python-brace-format msgid "Location {location} does not belong to site {site}." msgstr "Localização {location} não pertence ao site {site}." -#: dcim/models/devices.py:849 +#: dcim/models/devices.py:851 #, python-brace-format msgid "Rack {rack} does not belong to location {location}." msgstr "Rack {rack} não pertence à localização {location}." -#: dcim/models/devices.py:856 +#: dcim/models/devices.py:858 msgid "Cannot select a rack face without assigning a rack." msgstr "Não é possível selecionar uma face de rack sem atribuir um rack." -#: dcim/models/devices.py:860 +#: dcim/models/devices.py:862 msgid "Cannot select a rack position without assigning a rack." msgstr "Não é possível selecionar uma posição de rack sem atribuir um rack." -#: dcim/models/devices.py:866 +#: dcim/models/devices.py:868 msgid "Position must be in increments of 0.5 rack units." msgstr "A posição deve estar em incrementos de 0,5 unidades de rack." -#: dcim/models/devices.py:870 +#: dcim/models/devices.py:872 msgid "Must specify rack face when defining rack position." msgstr "Deve especificar a face do rack ao definir a posição do rack." -#: dcim/models/devices.py:878 +#: dcim/models/devices.py:880 #, python-brace-format msgid "" "A 0U device type ({device_type}) cannot be assigned to a rack position." @@ -4932,7 +4928,7 @@ msgstr "" "Um tipo de dispositivo 0U ({device_type}) não pode ser atribuído a uma " "posição de rack." -#: dcim/models/devices.py:889 +#: dcim/models/devices.py:891 msgid "" "Child device types cannot be assigned to a rack face. This is an attribute " "of the parent device." @@ -4940,7 +4936,7 @@ msgstr "" "Os tipos de dispositivos secundários não podem ser atribuídos a uma face de " "rack. Esse é um atributo do dispositivo principal." -#: dcim/models/devices.py:896 +#: dcim/models/devices.py:898 msgid "" "Child device types cannot be assigned to a rack position. This is an " "attribute of the parent device." @@ -4948,7 +4944,7 @@ msgstr "" "Os tipos de dispositivos infantis não podem ser atribuídos a uma posição de " "rack. Esse é um atributo do dispositivo principal." -#: dcim/models/devices.py:910 +#: dcim/models/devices.py:912 #, python-brace-format msgid "" "U{position} is already occupied or does not have sufficient space to " @@ -4957,23 +4953,23 @@ msgstr "" "U{position} já está ocupado ou não tem espaço suficiente para acomodar este " "tipo de dispositivo: {device_type} ({u_height}U)" -#: dcim/models/devices.py:925 +#: dcim/models/devices.py:927 #, python-brace-format msgid "{ip} is not an IPv4 address." msgstr "{ip} não é um endereço IPv4." -#: dcim/models/devices.py:934 dcim/models/devices.py:949 +#: dcim/models/devices.py:936 dcim/models/devices.py:951 #, python-brace-format msgid "The specified IP address ({ip}) is not assigned to this device." msgstr "" "O endereço IP especificado ({ip}) não está atribuído a este dispositivo." -#: dcim/models/devices.py:940 +#: dcim/models/devices.py:942 #, python-brace-format msgid "{ip} is not an IPv6 address." msgstr "{ip} não é um endereço IPv6." -#: dcim/models/devices.py:967 +#: dcim/models/devices.py:969 #, python-brace-format msgid "" "The assigned platform is limited to {platform_manufacturer} device types, " @@ -4983,25 +4979,25 @@ msgstr "" "dispositivo, mas o tipo desse dispositivo pertence a " "{devicetype_manufacturer}." -#: dcim/models/devices.py:978 +#: dcim/models/devices.py:980 #, python-brace-format msgid "The assigned cluster belongs to a different site ({site})" msgstr "O cluster atribuído pertence a um site diferente ({site})" -#: dcim/models/devices.py:986 +#: dcim/models/devices.py:988 msgid "A device assigned to a virtual chassis must have its position defined." msgstr "" "Um dispositivo atribuído a um chassi virtual deve ter sua posição definida." -#: dcim/models/devices.py:1188 +#: dcim/models/devices.py:1193 msgid "module" msgstr "módulo" -#: dcim/models/devices.py:1189 +#: dcim/models/devices.py:1194 msgid "modules" msgstr "módulos" -#: dcim/models/devices.py:1205 +#: dcim/models/devices.py:1210 #, python-brace-format msgid "" "Module must be installed within a module bay belonging to the assigned " @@ -5010,22 +5006,22 @@ msgstr "" "O módulo deve ser instalado dentro de um compartimento de módulo pertencente" " ao dispositivo atribuído ({device})." -#: dcim/models/devices.py:1309 +#: dcim/models/devices.py:1314 msgid "domain" msgstr "dominar" -#: dcim/models/devices.py:1322 dcim/models/devices.py:1323 +#: dcim/models/devices.py:1327 dcim/models/devices.py:1328 msgid "virtual chassis" msgstr "chassi virtual" -#: dcim/models/devices.py:1338 +#: dcim/models/devices.py:1343 #, python-brace-format msgid "" "The selected master ({master}) is not assigned to this virtual chassis." msgstr "" "O mestre selecionado ({master}) não está atribuído a esse chassi virtual." -#: dcim/models/devices.py:1354 +#: dcim/models/devices.py:1359 #, python-brace-format msgid "" "Unable to delete virtual chassis {self}. There are member interfaces which " @@ -5034,33 +5030,33 @@ msgstr "" "Não é possível excluir o chassi virtual {self}. Existem interfaces de " "membros que formam interfaces LAG entre chassis." -#: dcim/models/devices.py:1379 vpn/models/l2vpn.py:37 +#: dcim/models/devices.py:1384 vpn/models/l2vpn.py:37 msgid "identifier" msgstr "identificador" -#: dcim/models/devices.py:1380 +#: dcim/models/devices.py:1385 msgid "Numeric identifier unique to the parent device" msgstr "Identificador numérico exclusivo para o dispositivo principal" -#: dcim/models/devices.py:1408 extras/models/models.py:129 +#: dcim/models/devices.py:1413 extras/models/models.py:129 #: extras/models/models.py:724 netbox/models/__init__.py:114 msgid "comments" msgstr "comentários" -#: dcim/models/devices.py:1424 +#: dcim/models/devices.py:1429 msgid "virtual device context" msgstr "contexto de dispositivo virtual" -#: dcim/models/devices.py:1425 +#: dcim/models/devices.py:1430 msgid "virtual device contexts" msgstr "contextos de dispositivos virtuais" -#: dcim/models/devices.py:1457 +#: dcim/models/devices.py:1462 #, python-brace-format msgid "{ip} is not an IPv{family} address." msgstr "{ip} não é um IPv{family} endereço." -#: dcim/models/devices.py:1463 +#: dcim/models/devices.py:1468 msgid "Primary IP address must belong to an interface on the assigned device." msgstr "" "O endereço IP principal deve pertencer a uma interface no dispositivo " @@ -5447,7 +5443,7 @@ msgstr "Porta de console" msgid "Reachable" msgstr "Acessível" -#: dcim/tables/connections.py:46 dcim/tables/devices.py:529 +#: dcim/tables/connections.py:46 dcim/tables/devices.py:533 #: templates/dcim/inventoryitem_edit.html:64 #: templates/dcim/poweroutlet.html:47 templates/dcim/powerport.html:18 msgid "Power Port" @@ -5466,7 +5462,7 @@ msgstr "Dispositivos" msgid "VMs" msgstr "VMs" -#: dcim/tables/devices.py:133 dcim/tables/devices.py:245 +#: dcim/tables/devices.py:133 dcim/tables/devices.py:249 #: extras/forms/model_forms.py:515 templates/dcim/device.html:114 #: templates/dcim/device/render_config.html:11 #: templates/dcim/device/render_config.html:15 @@ -5475,62 +5471,62 @@ msgstr "VMs" #: templates/virtualization/virtualmachine.html:47 #: templates/virtualization/virtualmachine/render_config.html:11 #: templates/virtualization/virtualmachine/render_config.html:15 -#: virtualization/tables/virtualmachines.py:93 +#: virtualization/tables/virtualmachines.py:106 msgid "Config Template" msgstr "Modelo de configuração" -#: dcim/tables/devices.py:216 dcim/tables/devices.py:1074 +#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 #: ipam/forms/bulk_import.py:511 ipam/forms/model_forms.py:296 #: ipam/tables/ip.py:352 ipam/tables/ip.py:418 ipam/tables/ip.py:441 #: templates/ipam/ipaddress.html:12 templates/ipam/ipaddress_edit.html:14 -#: virtualization/tables/virtualmachines.py:81 +#: virtualization/tables/virtualmachines.py:94 msgid "IP Address" msgstr "Endereço IP" -#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 -#: virtualization/tables/virtualmachines.py:72 +#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 +#: virtualization/tables/virtualmachines.py:85 msgid "IPv4 Address" msgstr "Endereço IPv4" -#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 -#: virtualization/tables/virtualmachines.py:76 +#: dcim/tables/devices.py:228 dcim/tables/devices.py:1086 +#: virtualization/tables/virtualmachines.py:89 msgid "IPv6 Address" msgstr "Endereço IPv6" -#: dcim/tables/devices.py:239 +#: dcim/tables/devices.py:243 msgid "VC Position" msgstr "Posição VC" -#: dcim/tables/devices.py:242 +#: dcim/tables/devices.py:246 msgid "VC Priority" msgstr "Prioridade VC" -#: dcim/tables/devices.py:249 templates/dcim/device_edit.html:38 +#: dcim/tables/devices.py:253 templates/dcim/device_edit.html:38 #: templates/dcim/devicebay_populate.html:16 msgid "Parent Device" msgstr "Dispositivo principal" -#: dcim/tables/devices.py:254 +#: dcim/tables/devices.py:258 msgid "Position (Device Bay)" msgstr "Posição (compartimento do dispositivo)" -#: dcim/tables/devices.py:263 +#: dcim/tables/devices.py:267 msgid "Console ports" msgstr "Portas de console" -#: dcim/tables/devices.py:266 +#: dcim/tables/devices.py:270 msgid "Console server ports" msgstr "Portas do servidor de console" -#: dcim/tables/devices.py:269 +#: dcim/tables/devices.py:273 msgid "Power ports" msgstr "Portas de alimentação" -#: dcim/tables/devices.py:272 +#: dcim/tables/devices.py:276 msgid "Power outlets" msgstr "Tomadas elétricas" -#: dcim/tables/devices.py:275 dcim/tables/devices.py:1087 +#: dcim/tables/devices.py:279 dcim/tables/devices.py:1091 #: dcim/tables/devicetypes.py:125 dcim/views.py:1005 dcim/views.py:1244 #: dcim/views.py:1930 netbox/navigation/menu.py:82 #: netbox/navigation/menu.py:238 templates/dcim/device/base.html:37 @@ -5540,53 +5536,53 @@ msgstr "Tomadas elétricas" #: templates/dcim/virtualdevicecontext.html:85 #: templates/virtualization/virtualmachine/base.html:27 #: templates/virtualization/virtualmachine_list.html:14 -#: virtualization/tables/virtualmachines.py:87 virtualization/views.py:368 +#: virtualization/tables/virtualmachines.py:100 virtualization/views.py:368 #: wireless/tables/wirelesslan.py:55 msgid "Interfaces" msgstr "Interfaces" -#: dcim/tables/devices.py:278 +#: dcim/tables/devices.py:282 msgid "Front ports" msgstr "Portas frontais" -#: dcim/tables/devices.py:284 +#: dcim/tables/devices.py:288 msgid "Device bays" msgstr "Compartimentos para dispositivos" -#: dcim/tables/devices.py:287 +#: dcim/tables/devices.py:291 msgid "Module bays" msgstr "Compartimentos de módulos" -#: dcim/tables/devices.py:290 +#: dcim/tables/devices.py:294 msgid "Inventory items" msgstr "Itens de inventário" -#: dcim/tables/devices.py:329 dcim/tables/modules.py:56 +#: dcim/tables/devices.py:333 dcim/tables/modules.py:56 #: templates/dcim/modulebay.html:17 msgid "Module Bay" msgstr "Compartimento do módulo" -#: dcim/tables/devices.py:350 +#: dcim/tables/devices.py:354 msgid "Cable Color" msgstr "Cor do cabo" -#: dcim/tables/devices.py:356 +#: dcim/tables/devices.py:360 msgid "Link Peers" msgstr "Vincular pares" -#: dcim/tables/devices.py:359 +#: dcim/tables/devices.py:363 msgid "Mark Connected" msgstr "Marcar Conectado" -#: dcim/tables/devices.py:475 +#: dcim/tables/devices.py:479 msgid "Maximum draw (W)" msgstr "Consumo máximo (W)" -#: dcim/tables/devices.py:478 +#: dcim/tables/devices.py:482 msgid "Allocated draw (W)" msgstr "Sorteio alocado (W)" -#: dcim/tables/devices.py:578 ipam/forms/model_forms.py:707 +#: dcim/tables/devices.py:582 ipam/forms/model_forms.py:711 #: ipam/tables/fhrp.py:28 ipam/views.py:597 ipam/views.py:691 #: netbox/navigation/menu.py:146 netbox/navigation/menu.py:148 #: templates/dcim/interface.html:351 templates/ipam/ipaddress_bulk_add.html:15 @@ -5595,12 +5591,12 @@ msgstr "Sorteio alocado (W)" msgid "IP Addresses" msgstr "Endereços IP" -#: dcim/tables/devices.py:584 netbox/navigation/menu.py:190 +#: dcim/tables/devices.py:588 netbox/navigation/menu.py:190 #: templates/ipam/inc/panels/fhrp_groups.html:5 msgid "FHRP Groups" msgstr "Grupos FHRP" -#: dcim/tables/devices.py:596 templates/dcim/interface.html:90 +#: dcim/tables/devices.py:600 templates/dcim/interface.html:90 #: templates/virtualization/vminterface.html:70 templates/vpn/tunnel.html:18 #: templates/vpn/tunneltermination.html:14 vpn/forms/bulk_edit.py:75 #: vpn/forms/bulk_import.py:76 vpn/forms/filtersets.py:41 @@ -5609,20 +5605,20 @@ msgstr "Grupos FHRP" msgid "Tunnel" msgstr "Túnel" -#: dcim/tables/devices.py:621 dcim/tables/devicetypes.py:224 +#: dcim/tables/devices.py:625 dcim/tables/devicetypes.py:224 #: templates/dcim/interface.html:66 msgid "Management Only" msgstr "Somente gerenciamento" -#: dcim/tables/devices.py:629 +#: dcim/tables/devices.py:633 msgid "Wireless link" msgstr "Link sem fio" -#: dcim/tables/devices.py:639 +#: dcim/tables/devices.py:643 msgid "VDCs" msgstr "VDCs" -#: dcim/tables/devices.py:647 dcim/tables/devicetypes.py:48 +#: dcim/tables/devices.py:651 dcim/tables/devicetypes.py:48 #: dcim/tables/devicetypes.py:140 dcim/views.py:1080 dcim/views.py:2023 #: netbox/navigation/menu.py:91 templates/dcim/device/base.html:52 #: templates/dcim/device_list.html:71 templates/dcim/devicetype/base.html:49 @@ -5631,7 +5627,7 @@ msgstr "VDCs" msgid "Inventory Items" msgstr "Itens de inventário" -#: dcim/tables/devices.py:728 +#: dcim/tables/devices.py:732 #: templates/circuits/inc/circuit_termination.html:80 #: templates/dcim/consoleport.html:81 templates/dcim/consoleserverport.html:81 #: templates/dcim/frontport.html:53 templates/dcim/frontport.html:125 @@ -5640,28 +5636,28 @@ msgstr "Itens de inventário" msgid "Rear Port" msgstr "Porta traseira" -#: dcim/tables/devices.py:893 templates/dcim/modulebay.html:51 +#: dcim/tables/devices.py:897 templates/dcim/modulebay.html:51 msgid "Installed Module" msgstr "Módulo instalado" -#: dcim/tables/devices.py:896 +#: dcim/tables/devices.py:900 msgid "Module Serial" msgstr "Módulo serial" -#: dcim/tables/devices.py:900 +#: dcim/tables/devices.py:904 msgid "Module Asset Tag" msgstr "Etiqueta de ativo do módulo" -#: dcim/tables/devices.py:909 +#: dcim/tables/devices.py:913 msgid "Module Status" msgstr "Status do módulo" -#: dcim/tables/devices.py:951 dcim/tables/devicetypes.py:308 +#: dcim/tables/devices.py:955 dcim/tables/devicetypes.py:308 #: templates/dcim/inventoryitem.html:41 msgid "Component" msgstr "Parte" -#: dcim/tables/devices.py:1006 +#: dcim/tables/devices.py:1010 msgid "Items" msgstr "Itens" @@ -6229,7 +6225,7 @@ msgid "Cluster type (slug)" msgstr "Tipo de cluster (slug)" #: extras/filtersets.py:490 ipam/forms/bulk_edit.py:475 -#: ipam/forms/model_forms.py:585 virtualization/forms/filtersets.py:108 +#: ipam/forms/model_forms.py:589 virtualization/forms/filtersets.py:108 msgid "Cluster group" msgstr "Grupo de clusters" @@ -6360,8 +6356,8 @@ msgid "Is active" msgstr "Está ativo" #: extras/forms/bulk_import.py:34 extras/forms/bulk_import.py:115 -#: extras/forms/bulk_import.py:130 extras/forms/bulk_import.py:153 -#: extras/forms/bulk_import.py:177 extras/forms/filtersets.py:114 +#: extras/forms/bulk_import.py:136 extras/forms/bulk_import.py:159 +#: extras/forms/bulk_import.py:183 extras/forms/filtersets.py:114 #: extras/forms/filtersets.py:160 extras/forms/filtersets.py:201 #: extras/forms/model_forms.py:43 extras/forms/model_forms.py:127 #: extras/forms/model_forms.py:156 extras/forms/model_forms.py:197 @@ -6370,8 +6366,8 @@ msgid "Content types" msgstr "Tipos de conteúdo" #: extras/forms/bulk_import.py:36 extras/forms/bulk_import.py:117 -#: extras/forms/bulk_import.py:132 extras/forms/bulk_import.py:155 -#: extras/forms/bulk_import.py:179 tenancy/forms/bulk_import.py:96 +#: extras/forms/bulk_import.py:138 extras/forms/bulk_import.py:161 +#: extras/forms/bulk_import.py:185 tenancy/forms/bulk_import.py:96 msgid "One or more assigned object types" msgstr "Um ou mais tipos de objetos atribuídos" @@ -6418,29 +6414,39 @@ msgstr "" "opcionais separados por dois pontos: “Choice1:First Choice, Choice2:Second " "Choice”" -#: extras/forms/bulk_import.py:182 +#: extras/forms/bulk_import.py:120 extras/models/models.py:353 +msgid "button class" +msgstr "classe de botão" + +#: extras/forms/bulk_import.py:123 extras/models/models.py:357 +msgid "" +"The class of the first link in a group will be used for the dropdown button" +msgstr "" +"A classe do primeiro link em um grupo será usada para o botão suspenso" + +#: extras/forms/bulk_import.py:188 msgid "Action object" msgstr "Objeto de ação" -#: extras/forms/bulk_import.py:184 +#: extras/forms/bulk_import.py:190 msgid "Webhook name or script as dotted path module.Class" msgstr "Nome do webhook ou script como caminho pontilhado module.Class" -#: extras/forms/bulk_import.py:205 +#: extras/forms/bulk_import.py:211 #, python-brace-format msgid "Webhook {name} not found" msgstr "Webhook {name} não encontrado" -#: extras/forms/bulk_import.py:214 +#: extras/forms/bulk_import.py:220 #, python-brace-format msgid "Script {name} not found" msgstr "Roteiro {name} não encontrado" -#: extras/forms/bulk_import.py:236 +#: extras/forms/bulk_import.py:242 msgid "Assigned object type" msgstr "Tipo de objeto atribuído" -#: extras/forms/bulk_import.py:241 +#: extras/forms/bulk_import.py:247 msgid "The classification of entry" msgstr "A classificação da entrada" @@ -7397,16 +7403,6 @@ msgstr "Código de modelo Jinja2 para URL do link" msgid "Links with the same group will appear as a dropdown menu" msgstr "Links com o mesmo grupo aparecerão como um menu suspenso" -#: extras/models/models.py:353 -msgid "button class" -msgstr "classe de botão" - -#: extras/models/models.py:357 -msgid "" -"The class of the first link in a group will be used for the dropdown button" -msgstr "" -"A classe do primeiro link em um grupo será usada para o botão suspenso" - #: extras/models/models.py:360 msgid "new window" msgstr "nova janela" @@ -7598,7 +7594,7 @@ msgid "staged changes" msgstr "mudanças encenadas" #: extras/models/tags.py:40 -msgid "The object type(s) to which this this tag can be applied." +msgid "The object type(s) to which this tag can be applied." msgstr "O (s) tipo (s) de objeto aos quais essa tag pode ser aplicada." #: extras/models/tags.py:49 @@ -7899,7 +7895,7 @@ msgid "VLAN number (1-4094)" msgstr "Número da VLAN (1-4094)" #: ipam/filtersets.py:437 ipam/filtersets.py:441 ipam/filtersets.py:533 -#: ipam/forms/model_forms.py:444 templates/tenancy/contact.html:54 +#: ipam/forms/model_forms.py:430 templates/tenancy/contact.html:54 #: tenancy/forms/bulk_edit.py:112 msgid "Address" msgstr "Endereço" @@ -8068,7 +8064,7 @@ msgid "Authentication key" msgstr "Chave de autenticação" #: ipam/forms/bulk_edit.py:404 ipam/forms/filtersets.py:369 -#: ipam/forms/model_forms.py:455 netbox/navigation/menu.py:376 +#: ipam/forms/model_forms.py:441 netbox/navigation/menu.py:376 #: templates/ipam/fhrpgroup.html:51 #: templates/wireless/inc/authentication_attrs.html:5 #: wireless/forms/bulk_edit.py:90 wireless/forms/bulk_edit.py:137 @@ -8085,11 +8081,11 @@ msgstr "VLAN infantil mínima VID" msgid "Maximum child VLAN VID" msgstr "VLAN infantil máximo VID" -#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:527 +#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:531 msgid "Scope type" msgstr "Tipo de escopo" -#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:600 +#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:604 #: ipam/tables/vlans.py:71 templates/ipam/vlangroup.html:39 msgid "Scope" msgstr "Escopo" @@ -8098,8 +8094,8 @@ msgstr "Escopo" msgid "Site & Group" msgstr "Site e grupo" -#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:663 -#: ipam/forms/model_forms.py:697 ipam/tables/services.py:19 +#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:667 +#: ipam/forms/model_forms.py:701 ipam/tables/services.py:19 #: ipam/tables/services.py:49 templates/ipam/service.html:39 #: templates/ipam/servicetemplate.html:24 msgid "Ports" @@ -8139,7 +8135,7 @@ msgid "Parent device of assigned interface (if any)" msgstr "Dispositivo principal da interface atribuída (se houver)" #: ipam/forms/bulk_import.py:310 ipam/forms/bulk_import.py:496 -#: ipam/forms/model_forms.py:691 virtualization/filtersets.py:284 +#: ipam/forms/model_forms.py:695 virtualization/filtersets.py:284 #: virtualization/filtersets.py:323 virtualization/forms/bulk_edit.py:199 #: virtualization/forms/bulk_edit.py:325 #: virtualization/forms/bulk_import.py:146 @@ -8317,8 +8313,8 @@ msgstr "Porto" #: virtualization/forms/filtersets.py:189 #: virtualization/forms/filtersets.py:234 #: virtualization/forms/model_forms.py:223 -#: virtualization/tables/virtualmachines.py:115 -#: virtualization/tables/virtualmachines.py:168 vpn/choices.py:45 +#: virtualization/tables/virtualmachines.py:128 +#: virtualization/tables/virtualmachines.py:181 vpn/choices.py:45 #: vpn/forms/filtersets.py:289 vpn/forms/model_forms.py:161 #: vpn/forms/model_forms.py:172 vpn/forms/model_forms.py:274 msgid "Virtual Machine" @@ -8341,7 +8337,7 @@ msgstr "Atribuição de site/VLAN" msgid "IP Range" msgstr "Intervalo de IP" -#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:454 +#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:440 #: templates/ipam/fhrpgroup.html:19 templates/ipam/ipaddress_edit.html:52 msgid "FHRP Group" msgstr "Grupo FHRP" @@ -8354,7 +8350,7 @@ msgstr "Torne esse o IP primário do dispositivo/VM" msgid "An IP address can only be assigned to a single object." msgstr "Um endereço IP só pode ser atribuído a um único objeto." -#: ipam/forms/model_forms.py:357 ipam/models/ip.py:877 +#: ipam/forms/model_forms.py:357 ipam/models/ip.py:896 msgid "" "Cannot reassign IP address while it is designated as the primary IP for the " "parent object" @@ -8369,34 +8365,25 @@ msgstr "" "Somente endereços IP atribuídos a uma interface podem ser designados como " "IPs primários." -#: ipam/forms/model_forms.py:373 -#, python-brace-format -msgid "{ip} is a network ID, which may not be assigned to an interface." -msgstr "{ip} é uma ID de rede, que não pode ser atribuída a uma interface." - -#: ipam/forms/model_forms.py:379 -#, python-brace-format -msgid "" -"{ip} is a broadcast address, which may not be assigned to an interface." -msgstr "" -"{ip} é um endereço de transmissão, que não pode ser atribuído a uma " -"interface." - -#: ipam/forms/model_forms.py:456 +#: ipam/forms/model_forms.py:442 msgid "Virtual IP Address" msgstr "Endereço IP virtual" -#: ipam/forms/model_forms.py:598 ipam/forms/model_forms.py:637 +#: ipam/forms/model_forms.py:523 +msgid "Assignment already exists" +msgstr "A atribuição já existe" + +#: ipam/forms/model_forms.py:602 ipam/forms/model_forms.py:641 #: ipam/tables/ip.py:250 templates/ipam/vlan_edit.html:37 #: templates/ipam/vlangroup.html:27 msgid "VLAN Group" msgstr "Grupo VLAN" -#: ipam/forms/model_forms.py:599 +#: ipam/forms/model_forms.py:603 msgid "Child VLANs" msgstr "VLANs secundários" -#: ipam/forms/model_forms.py:668 ipam/forms/model_forms.py:702 +#: ipam/forms/model_forms.py:672 ipam/forms/model_forms.py:706 msgid "" "Comma-separated list of one or more port numbers. A range may be specified " "using a hyphen." @@ -8404,15 +8391,15 @@ msgstr "" "Lista separada por vírgula de um ou mais números de porta. Um intervalo pode" " ser especificado usando um hífen." -#: ipam/forms/model_forms.py:673 templates/ipam/servicetemplate.html:12 +#: ipam/forms/model_forms.py:677 templates/ipam/servicetemplate.html:12 msgid "Service Template" msgstr "Modelo de serviço" -#: ipam/forms/model_forms.py:724 +#: ipam/forms/model_forms.py:728 msgid "Service template" msgstr "Modelo de serviço" -#: ipam/forms/model_forms.py:754 +#: ipam/forms/model_forms.py:758 msgid "" "Must specify name, protocol, and port(s) if not using a service template." msgstr "" @@ -8579,12 +8566,12 @@ msgstr "prefixos" msgid "Cannot create prefix with /0 mask." msgstr "Não é possível criar prefixo com a máscara /0." -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 #, python-brace-format msgid "VRF {vrf}" msgstr "VRF {vrf}" -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 msgid "global table" msgstr "tabela global" @@ -8680,12 +8667,25 @@ msgstr "Endereços IP" msgid "Cannot create IP address with /0 mask." msgstr "Não é possível criar endereço IP com máscara /0." -#: ipam/models/ip.py:856 +#: ipam/models/ip.py:850 +#, python-brace-format +msgid "{ip} is a network ID, which may not be assigned to an interface." +msgstr "{ip} é uma ID de rede, que não pode ser atribuída a uma interface." + +#: ipam/models/ip.py:861 +#, python-brace-format +msgid "" +"{ip} is a broadcast address, which may not be assigned to an interface." +msgstr "" +"{ip} é um endereço de transmissão, que não pode ser atribuído a uma " +"interface." + +#: ipam/models/ip.py:875 #, python-brace-format msgid "Duplicate IP address found in {table}: {ipaddress}" msgstr "Endereço IP duplicado encontrado em {table}: {ipaddress}" -#: ipam/models/ip.py:883 +#: ipam/models/ip.py:902 msgid "Only IPv6 addresses can be assigned SLAAC status" msgstr "Somente endereços IPv6 podem receber o status SLAAC" @@ -9468,7 +9468,7 @@ msgstr "Máquinas virtuais" #: templates/virtualization/virtualmachine.html:177 #: templates/virtualization/virtualmachine/base.html:32 #: templates/virtualization/virtualmachine_list.html:21 -#: virtualization/tables/virtualmachines.py:90 virtualization/views.py:389 +#: virtualization/tables/virtualmachines.py:103 virtualization/views.py:389 msgid "Virtual Disks" msgstr "Discos virtuais" @@ -10393,8 +10393,8 @@ msgstr "Agendamento" #: templates/core/job.html:66 #, python-format -msgid "every %(interval)s seconds" -msgstr "cada %(interval)s segundos" +msgid "every %(interval)s minutes" +msgstr "cada %(interval)s ata" #: templates/dcim/bulk_disconnect.html:9 #, python-format @@ -12948,67 +12948,67 @@ msgstr "As restrições não são suportadas para esse tipo de objeto." msgid "Invalid filter for {model}: {error}" msgstr "Filtro inválido para {model}: {error}" -#: users/models.py:54 +#: users/models.py:55 msgid "user" msgstr "usuária" -#: users/models.py:55 +#: users/models.py:56 msgid "users" msgstr "usuários" -#: users/models.py:66 +#: users/models.py:67 msgid "A user with this username already exists." msgstr "Já existe um usuário com esse nome de usuário." -#: users/models.py:78 vpn/models/crypto.py:42 +#: users/models.py:79 vpn/models/crypto.py:42 msgid "group" msgstr "grupo" -#: users/models.py:79 +#: users/models.py:80 msgid "groups" msgstr "grupos" -#: users/models.py:106 users/models.py:107 +#: users/models.py:107 users/models.py:108 msgid "user preferences" msgstr "preferências do usuário" -#: users/models.py:174 +#: users/models.py:175 #, python-brace-format msgid "Key '{path}' is a leaf node; cannot assign new keys" msgstr "Chave '{path}'é um nó de folha; não é possível atribuir novas chaves" -#: users/models.py:186 +#: users/models.py:187 #, python-brace-format msgid "Key '{path}' is a dictionary; cannot assign a non-dictionary value" msgstr "" "Chave '{path}'é um dicionário; não pode atribuir um valor que não seja do " "dicionário" -#: users/models.py:252 +#: users/models.py:253 msgid "expires" msgstr "expira" -#: users/models.py:257 +#: users/models.py:258 msgid "last used" msgstr "usado pela última vez" -#: users/models.py:262 +#: users/models.py:263 msgid "key" msgstr "chave" -#: users/models.py:268 +#: users/models.py:269 msgid "write enabled" msgstr "gravação habilitada" -#: users/models.py:270 +#: users/models.py:271 msgid "Permit create/update/delete operations using this key" msgstr "Permitir operações de criação/atualização/exclusão usando essa chave" -#: users/models.py:281 +#: users/models.py:282 msgid "allowed IPs" msgstr "IPs permitidos" -#: users/models.py:283 +#: users/models.py:284 msgid "" "Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for" " no restrictions. Ex: \"10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64\"" @@ -13016,34 +13016,34 @@ msgstr "" "Redes IPv4/IPv6 permitidas de onde o token pode ser usado. Deixe em branco " "sem restrições. Ex: “10.1.1.0/24, 192.168.10.16/32, 2001:DB 8:1: :/64\"" -#: users/models.py:291 +#: users/models.py:296 msgid "token" msgstr "ficha" -#: users/models.py:292 +#: users/models.py:297 msgid "tokens" msgstr "tokens" -#: users/models.py:373 +#: users/models.py:378 msgid "The list of actions granted by this permission" msgstr "A lista de ações concedidas por essa permissão" -#: users/models.py:378 +#: users/models.py:383 msgid "constraints" msgstr "restrições" -#: users/models.py:379 +#: users/models.py:384 msgid "" "Queryset filter matching the applicable objects of the selected type(s)" msgstr "" "Filtro do conjunto de consultas que corresponde aos objetos aplicáveis do " "(s) tipo (s) selecionado (s)" -#: users/models.py:386 +#: users/models.py:391 msgid "permission" msgstr "permissão" -#: users/models.py:387 +#: users/models.py:392 msgid "permissions" msgstr "permissões" @@ -13317,46 +13317,55 @@ msgstr "" "Esse objeto foi modificado desde que o formulário foi renderizado. Consulte " "o registro de alterações do objeto para obter detalhes." -#: utilities/forms/utils.py:42 utilities/forms/utils.py:65 -#: utilities/forms/utils.py:77 utilities/forms/utils.py:80 +#: utilities/forms/utils.py:42 utilities/forms/utils.py:68 +#: utilities/forms/utils.py:85 utilities/forms/utils.py:87 #, python-brace-format msgid "Range \"{value}\" is invalid." msgstr "Alcance”{value}“é inválido." -#: utilities/forms/utils.py:225 +#: utilities/forms/utils.py:74 +#, python-brace-format +msgid "" +"Invalid range: Ending value ({end}) must be greater than beginning value " +"({begin})." +msgstr "" +"Intervalo inválido: valor final ({end}) deve ser maior que o valor inicial " +"({begin})." + +#: utilities/forms/utils.py:232 #, python-brace-format msgid "Duplicate or conflicting column header for \"{field}\"" msgstr "Cabeçalho de coluna duplicado ou conflitante para”{field}“" -#: utilities/forms/utils.py:231 +#: utilities/forms/utils.py:238 #, python-brace-format msgid "Duplicate or conflicting column header for \"{header}\"" msgstr "Cabeçalho de coluna duplicado ou conflitante para”{header}“" -#: utilities/forms/utils.py:240 +#: utilities/forms/utils.py:247 #, python-brace-format msgid "Row {row}: Expected {count_expected} columns but found {count_found}" msgstr "" "Linha {row}: Esperado {count_expected} colunas, mas encontradas " "{count_found}" -#: utilities/forms/utils.py:263 +#: utilities/forms/utils.py:270 #, python-brace-format msgid "Unexpected column header \"{field}\" found." msgstr "Cabeçalho de coluna inesperado”{field}“encontrado." -#: utilities/forms/utils.py:265 +#: utilities/forms/utils.py:272 #, python-brace-format msgid "Column \"{field}\" is not a related object; cannot use dots" msgstr "Coluna”{field}“não é um objeto relacionado; não pode usar pontos" -#: utilities/forms/utils.py:269 +#: utilities/forms/utils.py:276 #, python-brace-format msgid "Invalid related object attribute for column \"{field}\": {to_field}" msgstr "" "Atributo de objeto relacionado inválido para a coluna”{field}“: {to_field}" -#: utilities/forms/utils.py:277 +#: utilities/forms/utils.py:284 #, python-brace-format msgid "Required column header \"{header}\" not found." msgstr "Cabeçalho de coluna obrigatório”{header}“não encontrado." @@ -13994,6 +14003,11 @@ msgstr "Proposta" msgid "Assigned Object Type" msgstr "Tipo de objeto atribuído" +#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 +#: vpn/forms/model_forms.py:241 vpn/tables/tunnels.py:91 +msgid "Tunnel interface" +msgstr "Interface de túnel" + #: vpn/forms/model_forms.py:147 msgid "First Termination" msgstr "Primeira rescisão" diff --git a/netbox/translations/ru/LC_MESSAGES/django.mo b/netbox/translations/ru/LC_MESSAGES/django.mo index fd43bbda3ac817d41d29724a95600790682997d6..cf0d2757ca9e057297c5128be7e88d39a01c3af7 100644 GIT binary patch delta 65942 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 VYf+GyHCTx3Tajl^ao5Yh{|6T)0=WPH delta 66777 zcmXWkcfgL-|G@G4c^)&H2q}-f_ukoLQ?|0RGm`C2D1}m_A%%vN7A+x4O7vBzG)Oz7 zQlcUHzTfvbzkgomoa?&I_?&Uw_wD)i`{H>17tZ8QepxWfeF^@rWUfS_1YX)Nk@z@o zB9VKOrHRDgpA(4+I0P@jJFqFPz)pAohu{^zBoZSqi8=6lERVlnZY=z3B9R?SV_vL` zlqVC7NED%<9ny!yAk2@W@iClB3T26t6-f(GTdE zMn6T@&JSokzoQ}k2fe@Q`7rW2=yMIw``ex;{)I^Npr9a*h!>_u7oe+k89L%e(Gk5E z^Lx=q9YH(%WAslfMLzq#p`l9X^?GQ1ZP5r^lZ*vJ&<;#QE1Zu$a98vZ^ug!QwX!{y zpFum8aUonUj6Po$jaZFX-Y}N8kL5jMd2&F!Fb4DUz*My1+hX}jw4$}~`qOB{w#Mrp z#PZLv2<1PaBTK&+I+PF1Ux{|K3L5%mp*)%BNWwYkjm2+{fEbUQi)_uzH-aLk`a7iYG#v{WSWVh-9*WRkGQmCy=?p&c5Jj(m2^ zFGD-@C|-q|umqk#Bb7frEpacFz%}?hUW(l_LOmm*GjKKKOEKA#M3pRQsiSc&x?K*T zBmNu>^;c-^v2fcV(f#4{yKEgj72*<5q%5J#y+?M?Z{<$Lxf79 z5vYSkt|eMuZ*(yZ!h-Jq5hR?W>G8rH=&HUax)oi`-=h_r!0Yiex|q7>3#+;x+QIA5 zhKHlqC!-ykg^h6`+M%78#r^*=2`l~_eemm;|229JTTp%>+AM#F$Q*Pn+=Eu$UK`nwfm{~P*66gVdjqKj*7bR!zVSJ4RV#M<~SHp73g3N|d1#_>!? z@d{jnSK%929{)oZb@@xv5~Zcc5E41&qL@r@pQPJOl%=xkKRES&qwH- zeTDWg`{ikg!B`T9<1Kh4evbn%ONkJ{!DvTDM{hzGX%dalTC9m1uqu9z+1&rxuLw8f zMJp(Rj;s_ql6q(dTVXrwj&8gA(fc-_Q@9 z6WY^j(S`@14Ua)XHa(W#9?Mrm*P%!43+UR|h2H-uTF;MYhyOr3oULT2w>T#4c|{U7 zTnimRBQz2%(6w=OyuKh_UxKccd$A|Jg}$B(XNGzvqV-NkxA#1BYVSom_6WKgp3P+c z+w{&d zc#R|pD`5KW@&agk z$#NvTp)q$hE-idbT4fMHp&=DO%r}A60;d9YT%7u|%iPm2&+9Z_o z`JaRp^^J~*H%yNCTVj4`bX9Z%+Tg3`NcN)j9E;aap^^Cq?NILWVNqtHi@Fl#bpH<{ z;fThg6--5YdTY!ti}}?t{|vfTUPBw$f$r-A=>OHet_oqwilXh6G4KAb zLc#{?ql>Q%x_x?~b3QO$9~rOTgjP5|mM@F>N6@wO3|7E5(T<)#>phJ=_aEA^+!fjX zHe8B?b6f+xp$YnO>44SocJ#=71s(Cn=yN}!pLouq9sVcgGb)9Aj%We21I5t@RYE&f zqZ0ey3hGnfYHos#xLve6+Ohu8k!VAc(dTBN_umq)--!-jWz0VouRo7=U`s6DgXPJ8 zREhoXCz12wlqBkx?_4 z4{iAJm@kb+usXVCnnv5A^>@Ww?*D!y4Bd$6wA2ND=|mUPeQ3k$V)^D+z5}i3eY^?} z$LmFFg$R~KLw*g~(NS0yZ^AP8pylrW?IdjAJ#-2__X7SL%l}3zx}L2vvHt++{p zuz%a2BbtXs;x@E_73h7BqM>~XGw}uVYx@DT-c#uPMH_}iTm~(#gnk#iHc6r#iDl?i z97ey({fbtctx;$=FM4K|h}J?YXor3|ori{YA$GtO=-eMg&w(G&kzYXTsogl#pKMR! zH43_6EzHp*ocWE>2S!I9K|iw{LQl3!nub;19{mQj2&>~hyb1q7BYI=AP|rK)z|N!F zxnT2D2a<`RB%Iq!v}YC2-Ov=>4P9dSV00u?u>>wgzw14PhWH(H*Bn6WJBl{+T`WI| z?wWJad@YO+`>!GiLtPtvurV6SmgouA9qrKIn4g4pbRN1UR>u6}=yO}p=k}n_eTv?9 zB3}O!tCG)l6&-i~*CFA^nxiAQ#tS$Yt!Qk#J_`-uJ?MQ;p^Nh+bTMv;A*+WBIq}{USCI%M6MNzfU!o&DjgBm5`>=S5p>My6=v+2MulGlH!x*$< z^J4jOwBwJV19=*az)R@1-GO%SBTRk&|BQrl^%XkD=g>=yu$teEs2_#)-jxHwb71rLqj|W9l@BGpBk^< zicZC{SpG12-v%^7FQEf@J6``JmVb-Ze+E;(|D|0W9?XL_SQMSRa_Gpa$LkHzid&=i z^+G!^7`=ZSdjC{(#K~B`360cN^gP&!KL5eh?0E%iRngH~7_{RY$; zy*?Cu?nZPj=b@orfWDURM%T(-tcSm$A6zPQ32R~)K2Cm2l0;t;`MQRRN1^+CD%!v- z^!2(FT}&Uw^22E8PsH-G=!pMCN0_TysPA%g02R?lHH-O9=>5sTBz)jTbX6zO8<)i! z9zo~!S+oPMpd;Fjj`&RUV!WQSdzkC|Xnlpz0hLE1S2fxwTu&z2g+!tcI^t0=KQrbR zql;=acEa^o1J9sGahV>W;wtEU4bhRcLOaj{jnq&y5))#6J{I=VxY-xKJFK0z1P z*Jy{%q7lg6GrWZIN86$un2DYrw_;g*0R4)!9V_86%*33%!e_tA=!e?gc)9z3CW)E2 z5?wR}t_c-1#7y%2u`bTS2KWM2z~g9x`L9jmA1V2v60P_;^y~Yr(KoRx`9$yVDZ3jw zz@?b%MPe<9T6h+(!3urS62ovdw#2W{Rb9Go2;m^K!O7@Utit-ZFPh#j?Egk+WXE9} zT#ZiAH)v!}^<)1#R~IR;M|t{(21=oevVOE3`e1K#5e-JCWPHru9IwwuzXvRh`3>mY zZ;s_V(C5EE7y0r2?0@I{90i&-AgtEhn1y^ntc;i8c5H<HY7BZA8p`rbj~u- z{aia=zT-bH8CaTpGJ@9ZD^!FK+cn7;&T%A=xcPYzDIlf8~VV1XoCfZ zhU-Pq`^%zJQ4y`MR?Ig=JJc2(NYCiNczq<=&W)Jc{XZ+-un2AVUi7Qj8uY|_4J+Xe z%)}FD1(ys99V-$ofp(-cdc9iAH$o%W2Hh~BM z=%?HL=p1Js9y*W@{m@w$9Y75nj?FL=*I`B6h3<}1Xni?Gu>XCa00|o`iFTkyv@upC z-xl4Tld%KdhOU_p&<HB>+5JE8|tf2@p)Z%Bp_ze0f#cr$tc z?djL(YW@X{Kw?zr;H7Bf8lw$&Ku6pet+*FD!nx>nzCF4Ut@nwT-;^YgY8Y+cK=ga` z0Qnn@M846Xfy>c+X|w~?qfODNxEh_pYtVYHkNF$X`)9`TMX@~j0112a7+UcQ=#AUZ zp6^9R^cl9nEMvmQ?v7Z6{9v4mOVE09jSZnMik^&>qMgx5-iS`|JS^`1Ur)k@-a;44 z0W_4Kp&j}vmY<5{f1o2hkFK4p_>i1%pXP@_!eC|f1vf`xG_Ys2wKmTXy~h;k!}|4j7dW| zfP|qRfmS#kJ!s~l+vim@GJDZav3Vwjx$TJK$dAL?_!;^^CHJIo(zV1|ObrN~(!EpI|8BDnDDcex9?f4sLt5yjur?~7p>K)K-756{^)bH#ZEzpD?LJ34 zcoJQtf1(|~fKEl))Nno&N|La_66jRaM(4a~v^~10dZIn%{Ufq4Blyo!7>{pJvo)@Z1Eql@Tv^aIF!Xa}E08`>GKe}JXQA4MBZObZr3 z?<>r{g&KWeJzCKwG_6@!k41E(S|=kU#E!~p}`VZgKR}K zl6}zz??CH$Am*Py7v)Qs`uo3av0yjaWZzg zUvxE=C;v4%kQ{Tu8p?;h4a=efsD@r|JSV>YTgHO+=$v;&JJLVq$3~~29hx6~4DHCS zczu8Lb2P+1pbhSr=dK>wp*At!9j)kk^n4hD zK0h~>FG3q!9`g^Oi|+|EqMOnB-a&WQ!6XSs^dnZp>~zRb*F=Bq>WO7=B38u}I0fHC zJ6i9Su+Lke9l8dM+$eP0&WPTPylxXuplf3%8j0jTBwCWVWM24FiO$%D{F3MabOc3i z4VFbmQX3sXTXaPI(bw!qbUV($^0*T1=yr5U_M-P63?>s_lCXi3@rJ+9iWBpLg`<_D zEzreu4f-v25IW+A(a1fEuI>-eDf|d+@LP21e#P4O4>t4tUu{9SVJ!OKH1z14jrRC1 zw852l4L*ui^aon;e^?!JEevnB#%RM6u?{BDcfe+JO1H%PVNC7+qa+-`4`>MgL@UU9 zTNu$5=v=ixe}w9WPQe7U!}HKZcpKW`WoU;VKqI>reg1j$z8%;d-@~Mfs`%~U&u-ge z7xE9H75#|rieJ$R&qV)?W?dA@^F}X=W}>^G657F0cmjLi<#&X7#-ov)hIVKXTHoDhd-vVJHnQUN z6gZcg&zcc)eA$6FQ=6&`1qM@0*NvU_Ls4`(pXyvHazje-~XlpQiHc z|6gLke`rq&-xWGi868<&G^8!jiaTI?>=Vl$#RcS_L_eBUxjPJ~I=Y=3ps(Mq=x&-6 zuTRC)zyHl8;WoJgZQ!nW!|GW6BpR7#(8#=ucIeIM9yAgkp!Xd^7uyN+{y(rQCYFWk z-Oz~j!(@LFV@O!RZXAjqqbFCxd%{`j3c4UQM4pyhM1J}!#xh}X}dYanfT=y*QNBwrGpva8Tg zcR}mvfevs0+VQby1Sc(L{~My|6j2MggFYWIcf zjnIZVqxB8Mt8fhZujbF-J$M|aRD97V`aHkc6Qvk5*6z8)HkXg0ry=K823- zXS@oFuL=zeKqE3DIuY>mu5S1B=nAxF0=$p0b?w6K})|d*g*Kyg>PH=dwGy?t6dag$YI2Ik?^jLlyCM#2LFA3d_hU_>x1;3*er>_nV z7Q%AmOQ9ofk3QEkItT6OQ}Oz9(Jkot@iw~Fj-nm=ZZ-SggW`0&G4~_k-*_%Vzg`c+ zR4CC0=c6OO6CKHe(e>#0@iIDqJ?Q-h(Y5s%I(6Tm9nSY?_!pGMkFx)L{SKwT8)u^r zF2qi_1YPx?qO1EPw!%x+gvfM>UWYb(Bi6tfF~0$=Z!_BA*U_oigAVNDBnd-#6dl2F zH00;ehI6kC`76;5)`&JlD{dLDcZlWv(1wPhk(!8xI*G2O#aJGnLf1(00}@vB5&Ga4 z=mRIBXXEv>b>Vsr^m>tKY4rK(=*a3}TWp1na1pxNm&NN(q782j`DEfX5{7O!TJZ;H zgIYc zec-`Z{unxvr_heQfHv?dx=VJUtNsw$fzQzUe?q@%ox!WH+T-Et_yjZ}2Ql^c|Hnz# zvtKO0^XOcsJrVMkpx5)G9k>E*pe%Y{RrI-f=oB`I*E`014|FYDkIirb8kvol`u@M2 zgsXNR*2eSbCzt9^hLN^KL)HU*8I43cumJ7Q610KkH~`nh@{8zwS=Wb%=0O|241KQR zdiK9BgBlb#1+CHT*a2(e&{)14ostL82iKt$Z;1I9(G&4CG!om;df!4j{z<(46S|gu zMeF%zeKLeL?Wr(#dC^d3q9dvn^G(t1*%9sNz~~Tk3Pz%#zY*Ku9JIr4p&fl6ZSW&> z?R(|EXqp=+2)1s@<26v$i?L#AQ7_IMA z^dEF8k~y9Z4HiQSszsYeyP+KzjE;0XR>Ygph8{&DvL0=4Bkso6uoljJChUS2@HX-% z(KR+{V=7X~#N#A7QE&o%7u0(;G|&SVlb?wXVYcVOYk4i&fg@-GS)UId)f!+`@)L0k zu10^@$nip2Vg+7})$tfM!Mrbeq_h98CgG>oG3X6l>HxSE{pNpOG3A_=1$DTO!HTM5-5-*eR(`fN6;ZyB$ zbV|O6`3kRxUp{U`?|U14uG!YK)PM6~9i~FPEsX4HG{g(A1@6RAnAjdpz|lB`?Ekj2 z|6RQ;-v}eV0mqSl3Z3J^JHobWjyBu_=iy$ggq?SWFQGSM5Axg52;_S+{CUkt>`p${ zTj4+(h`t4kbg{oiGGSR>2Oj_g4r z@H;x^h8gnzE}Wy4hY%0(G+d{%nzKy=!_Qd=T*o*v0blWuF z7ygmzIdsmC;AG79eyDf`TD|~1>z_kA`Z7AO-B6g-^x&UUU&(#N<^ZT7D4D{#(%_ zcscsO^XSQV088R&G{pHo40Bx!ow^F>i0h(L)+E|GmUlv@peOqLb?Cru_>lcymBefc zT#Xyh(7cH*ns?9#KSWpa5zLQ2p$-3w-k0-0THhoWod25g68WBDeuL$6}0UUbp@FXlf*BlKM? zKb0imT>lww$n#M+!HS_FnuuOsh^~QE=we!nZnw>7g>RyZ?Y(&YGjyB%6tDk>b~yXT z!CcW~J`#qk5Zdz+=oFMkdtL<%d4044?a}XYU9dWiM5pdPbn!lmzI5KjO#B6VV7^bn zA{~O3&qdZ$GVv%07tap#_4p3jfxpp_U5xo`hrc^9_UMFHrl{uw8C9zMW13#{1vNV=I3EobVaWZLL+cf zbT-Y^xUm*`BDO&*9*TbA858px&?$Qn9pQKA zTDpKvX|^N5JeW!TQZ$0~@Mdgzg#Evl#A_5B#`#CX$u!{0@X_lLbdl{x7vBZ6f{bG! zpAX#yh0%t~pi@{C9cd$U7j%yK!RY-{qVtX=Lul@%z`1-RUU)vb9sLz-AJ)Sk&=a!6 zSD~VYXvf;ceCKF?GyMkX+7E4Lc)UIeo%4z4^GP&9i_r+IK%Y;pi;0cs1FxWSxf?xrzC;`T0}W;N zZ^8j|IU2EQG2cAe9bLRb(S|0Y4c~$`cn3PL<)J*8c!-1*J%-NROK3>njJ}IL_&;={ zhtUyzgPw%HpdCzn8!9e@)>9nqKm~N$)kB}}f<~}!D$o8OOu~_kMR!3GtzZRO!CG_# z&!Z2%7Rz^{5!;X6e>9e#MDPC#9Z1@D;koQ+c|NqmMKLGsC#sOJXZ6s~T!mKL9t~yh z=twkDH^=<#F@HZ=;ZtZwwxUzF2R#?QLikv@$HCE~e_P-T2roe_fpf_HF z&iN2DGGozq!p*UKRxDo_^UKiZA3~phGG5;t%ilns--~wa1GHm@e@KP~j>m#CXhm6m z3{#OG?Z9Pd1WKcks*m2+DcT2Zcv#F&jQQDUgLk0y+=CA6VKjnICP_HwFGshbi)I)4 z;CpC<4xkkrLpyL1-S7XQ9mzNz%JZRfUKXvV3Hn@1^uEsMfz}t@=E*4}Y-kC(*jAvS zc^v(6`7%1EpQ06f9rHh<4V*(q@-I5FTqlBs(TJ2pJ5UvkU;}gjosjyHiNPeiaU5FV z479-{`rsl=9T?~wJ`~FzjXsS&|1#R~x6pIpP%Qr*?fCC#2QNl*oV1+%S1^^}&*ae6 zUK#CaGqeLe&<+hmM>-;2zX=`5B6L-+Mnn2C8p-`=gI`9EqaFP{`X8o#|IhtXnB$^o zL*>!At$~ib4m$G2XvHniHPH>dZ&18G1dZTGbj0J(2B)JPS%}tuH(Kulm^ASy2_JY8 zZDmr;1xI4R@#qyD%a5Z|`5W5dzhimE>F```bO43XftF9@+5Zh=K}Yn# z0cc1^#QYfa!JE(r7sm4Y(UGi0L%lJUZ$i(JZP5?Vsrm-J{~S7?wBP8k@BbVm3{`$~ z#8;v>RzpYJ5baP)w4rurg`Lqz3`U+fei1sLl`+2tt^c{0-;SyMzb_UXL__!$ z+JWQf$j_i7&+>cdP!V(lCDH5U(GE61pKFRvWqY*4z2fzLXos$g`BA^K|Gi;61&(A2 zI+9z^p5BFCUmeRILp%6l%x^(E@@{lLdLn*|*7q6Okso6IceJDbq3vWj!~VB|f@i`5 z#n21o(T3_`YDAbC5n4enbR>Pz4h}_sP#A^QH#U|}j``{6B2UKr3iSC^7TD7#(1tgm zi)SY~$A{34{D?OEE85^6=>7ko+b+i+;rf+m#bwZ`sfOO)JlZCfUmf$w0VHf-2)gY? zqaPqzijZ?F*#Iv0MpydP_k--VU&EY`!!Kf|};-q^$U zf09IRE*!vySn;p$+wKr-PyRu?3cp5wW-I-7umzSOKM*V6E$FY_PvH&t9bSc<{|SFp zvlMHSKOHS|p0(xv?@hv<55ZQr3hl_3*aI*7H~jcC8f|bT`sML;bY!0)btbZ02)_&d zcQJg%tNUO0nY|mXc0>69J*L@W=l`~{$Da%dNOf0 z1QAH}&6l29WEuI>6SKKc5c}iZ zI0{cjdld)`ZNsUQS1p*Hn1<`n3X2yCBU^#p$>+E8am%>!3GG!>YI%-DZ2S4xWhDuPBk8+J>#8 zH=x%SV_DpRcHld7V1=&;^(E1cZbYa2ee<-RI7`B>;f1dZ7wVw-f#?Wlq9I-p%Xgr2 zc@&+3+$BTBbx9ZnR^^&~qYh=}_Jf3zF}NHE;yl-hHLn|C34VqF@2mFO#17x7)2~ zdHJ&Gi6QtPx+~6ODa=+bR9q3QxE0#5Yte7FQ_&7A!%p}*dU9qfAL^-$F5-ISlVP>C zq2Lh;u0`K|f1n}DRUvGjO6W^!D9*u~k=2{{1D%4b6+;8n(8%>b*U&V45Ff`b*t}AD z>eulln45jLIZ5Jj5<4r0IXhA%%vqkQp@LS}fa^Eni&U__T6*FF_NyMYX`dRw;pmY( z39rTZH~>Gxeps(&dg{-7??zvC$I$~U*`ihm$;XDZB@XTr#nlL~7AtCKt{}OVkf@+YDW7 z-O)Lm9bFTB8x8ew^uEFk!Ww9aF1CR&zXV;(>(TAE3md!tPsJN5Gz@ds3OzDM#r#@q zLH=E|1KAse#Z?8zlD`()4i&iDz&<+Ob|O z+5e7sc+2pzxeYz(HlQcjM`({vqW9%!73Q!qx_{f@JiHc1;$gHS&0D9ZJ}q~|Rph&( z=fp|$<#qwR?~*pj&`{wvp+{AsO>q!6bV29-8T4TJ34JhI+ps$dp!q83i0h-fpatHH z+tGp4Z5JYSHM;mFpeN@2NfN%dUqs(Z@1P^S7|m=Srl1uXiHYdj@UG~S*p2)fI27}C z2o+C?F2*L5KY=;&WNgPAL4QQwiqBnkz!|-=>&U;@SreH3*XdgmH z@;bVPK0@dA9~_6dI)zV2)6tJ$r_uAHOXqa|QYx7^OyW)oN^}Wtr}fy5{Lj(4UDH$l zqw`t#Eah*b6;JLKLVXN9;Rbb2PrQwe_ef9uhsbjG4DXV8_%!8z;Bq%(VmDO-WQD>k4b-ib!!06JB_VQtLUCq1zUTcP*u zL+?9+wP`=`FNxt;qi{;wG_*IMp?nCf=XLZY^BLZcKcffK!lB_H+JNq!qnH)X4rTuvs`K$ewqfChE71n( z<4hclcHld7&VNQjm}Piqun-!l3ek>eLu1j%&X47fVtMj!q94tUC*y@HMuZ1iprP%9 zRq-aYg0*O5wxgf__oE&9FqkLL)3EJRV z^!X3a4*w9%Kaq0UPc$Y`5qqE^oQdxH2e3MB!kYMXyk2lpSoJm0>)p}oNwlFCa0I@K zPEEDRVOw8??v`O_WaeYi5o{pgNDrV>aVA=CN+_QeeGnbN)|meu-Co&m3LUP3&Ust3 zLqpJd??qSrOX#ZqBIff?W&c}2gQ;Qv4nRjV6J6~aa5TP-j->3(VGT4u7g;+rQrE@& z^q60U*84QNOZKB*P7k9U$T=-cRr6`=e;evWfs1H3I)Y{KhK=Z3@J)2_?ME9tiXJq7 zqR;1=9xQ=guZBjpJ^Grx5e@yqn13Q(-9iditZkAx@ShtV(42Y!hD9nC#EM4}`* z!m8-V+M!c15M8`Cp(DK)y>Am5!S~VoKSS^P6}gXp|D6*?R1Ll1S~L=4(F$(Eez*oF z;y-8wW9NodeFwU@HewSzgwDA??YJ9?qW9N`cEKs+$6{~a|Hnuyrl9^U>8bxF+gs>% zykTBgB+JkdJb{*PLPz`sdTyMFhVn^R6W_s3_$Lm+*7MU-KSZv@)ZhOe zCgG8qz94+qtc_MY8T;UBY=>uY2{v6A?tc%>|BW_q+imHI_wXQA#fNVX&%cL*$(LG` zp86B)TX6yToOiJQT@-hbaG$QmR=5NG+@5}CSTyyqFZs6U3HS)Q>JLYcql@)#w7x4A zhYu`O(ffKthoWm^3VKp5U(Eh@Up_^FbM!enXMaWWEeREsN9VW`dH_vEcfrHxb8n;D z_8YW;i!p!Y(oo(Q{RGt;SK%zQzRT{4bKm@~^wghT?L{j}yF0u#ufU$GxrD zJLbJF{N!2|JClDES7Mfx;owlLeuNF&|D_)eBkPZbXg#_cwxQeZOSA*!R)JO6}uS-w; z?|SdT>$!du2V%3w!f!hdp&ifvc)0&o97X;U^jvB21pEIx68DpskF%c)M{Qz#Sl#{6 z#kDm0P;@;SftS#co%HJOgd;!I*y;`;-3|2V>0_!YaN8ovMd%3BG}Lpwo*XV*SzUJJ3(TUt>1R zw<&DDA~@dn|8NqSTsVjW@CHSzw;_6U#~Xz$56QdIp_}?Pv&(qHnwZ&{bREwP0H`GQ-g4 zZ^A4%4_&Ocp>w?ft#?Z--xte&!PI~M`yz=d+>m`sIGgLBt8*My#g*s>lwDXE&tNmW z^7ZhsdLY{2yU-3khpw6T(Ydd%HSC6K(W#q_o)0Uxvj6S*Rtg-+p6F+22Tr2bFWnYG zRtjs9uZcD|BsvdmcrE%tWCz-TZ_%l2zCAoY9F6D{wBGx+v;T*ZSVw^!$ofY3vRMRO zty3`_XJa=^Vh8*H{e)C>N7!DS(QQ05It_j4EQzj1>wha=KNLNlBwwN6A@V8m}c^2kpQen2D#*gD%g3aL_e6kPM4$G6fyE@Dw(}-?0T&Js5tLABWE6 z<9Gx=K(B8+6g~-^LZ2)3QTXmR0Q-=C0b5~?k3)l=8*8^(f+&i#QQh``AeBZ?e9a!YX2BU+!8%tM#OwF z`WQB({9UYyiR0nEYG|a|N5`Taz8eSO8q7`miHjs$y}3>VE8|r19ntN$13j4b;52N1 zlD8lo+=1?b(?5mCWjz&MT1C-_mBg&r1WRK}bP9)~2UHSM|NggYLGN3G*86(&J-m$kr>B#l=ci*q z?%zUB%i>^esD?Ih7aEBNu{J)5R`3}*lHX!J-|wNm3OJDR)@Vf5po{xO?1pcnYva=7 z8UDGQxon6%DY)v7;Bxff_yO&CrL*Dv-4K1SGrDFbM(;)su;=3SztC-6;#`Qxedycu z6gI`2e}(}i+mUdDz0eA;Lq{|x=2xL}`#L&;Pw-(pf&KBezd{GUj-JM5l>dj>u-V_? zQ*fJTcg&!CATlui{-1;+x)Gb>@|Zt}71Mu-cDeDe}KZFFhYtb$#?)=z*Re zlh7$x8?RqP2U_gk@GdBeQ{4Y;Nw`S1po{DfI>+bH#a8`7*bO7lMS3^Z#J8hou|4_n z7sJUm0bMH#urb~f%il*k{tenrmj4*I`@bp)KkIczN3Z~G`0-f&MJ&%p;EYrabXW93 z-;(pt=bphKxDOx0YH1m%k9G&}4)XQVGZJ}NYdg>g{VyXUk#x~qAki1wXURx?5?X_H z;74?hugsc}T3j>Gk?%r7`3E}E|IiPYxwB=YR(mFTy%sv+rs)2^4n44LKo6qX*)qcZ zzn=noz60A~sq7i4f6W?&HgFmT;RQ6L19D`f{xe!D@jmhub7rI#=No9l2XF))K@YZ8 zmxOv2ql^22n1AIG{`ZfDa2Ew0kw3>9^5@D(eQ~IcmS2MvaT*%xHRyPMnq z@J{l53T32zUi$_|nZGn6F&KB^Ls+UXiwL)&FR7+QLPWNr`~MJnZhVe@9{4%tFTE_a z7MOn$j-^EP)4`0U25*evaub<#;qb65YoypsRm( zEI%5{|BU9&3>_(jer#`!PSIoNk$yCj{hvvqcd4)orlG5N0ou^h(YMhx@+FqUf6`dsA$osTEQO<@cVc()8_{ik0bK(X%4DSe$){PFWVqoy3OtdDmkkxwLp#z9T@zEI z3!=->k*|xs9zBRg?j&YnzH*_XHSt>Vozca)4trp$WcjddR^Sc_3RMW(>kuv@U$>Y+jfT8FTF(IVWSoXB>Q(66zlA=33f-O;Qu!p` z_iKa)N})$@TXZ{3iRDkCq1}sP@E`P~8&Na79lt|E-J(`T>Icllcq92Au|pasX6-OV zYwBdAzC*r)9$dd+71~c!ts73ZzSxxf9Wnn7E+PLXI-&*j!V&!xKHzip!<_EMrR4iJ z$Vh##IF2sf{0&2c_0a~0p!GbAu92OX^pne9B>Ll3jlv>Zg?>WWiq6@$(etsqP~#B7 zCRmf}UC{=T(buDA(KS=ENk;0=_M4%fF+W5Pq+gq`|8tON)-=pvJ9G}mqO1LOoP_If z3l?h@8a#x~VV33@sehEZ3|$Mu(fvIg-^aVqgK9vF;6!u^7veHJ-Gcq^BAa(rM&ed{ z4DC?)mSOSKK=*%dw4xDc$7Z6ti1IqRr@A@d!G{zn~4CM@N>eedtIj^c<*( z4rmMx#W}dm{r?pSd%U_sSgjk;iVmY4`vwj18FXzF>lmi29(n-viB3hYFONQtF5Z1; z$iKzZI{4+Uw(`(uD{V97V8ooY=B0h7e0c+u@`1^4fpp!SM@aXp!x<~ ztOdJ;hAQA<@{Q5m@eVpgM=)u@pRpiM_b>$&(H{0fr)FeyD*8@X5M6~dC1VVsSf28Qc_Xp8>@?ReoTS=VGT(`#uDc?CbteyTt z!u2^=%KiTY30LpC=xg*_dm1SJ`_L~RY=a-}9jiRm4 z2z5dqoD;ntXOMpp`(c&w;TMmE*pvJTbbw7Kgk81^t^Y7OMQ10({a^UTFoLS+DsO_` z&;y<0N$7T2gU;QnvHT;&m?c_xSZDxw|k8l8Yf;4XAZzD7T!{)64H-IUNyawQ2z z{Cf05Y)bwIbdJm16gpNH8@ftIyNXe0jE-)L>K2-Y>k&r&q)2DQeU(q zzeWqp2pz101Kt14NjRsg&=J3eo(JzmzehWmHZ!c|yl4dKql>Y3bYk>Q^!~@OGQNmL zn~_Z2c$9+j6#R*f=t{PwJ#LA9x9f-5a4s6!g=ho!qKj)I zx{G$AAwGmg;#ahu#4RDhMbYP*qLJ-*OMGw+1$R-f44=c2^DIj{~#;3W&gXTFK(NY|hZzJ^WkC$zx|w}p|{$JAQGGn6N9AYteR+#cp|0y+gV z&<~Rjqu1X@dwdL?^9$(IE=DJHw)Fh}P2uUA$A!qjxqs_sh`(Zw=n={(psp zJ!`%=+|VW3AKfN5pbgJJBd`jM&=xe*M`C%tCEdcT$z#j=eRF$JlAXAAHL47!QSKxKEVFpL}K&<8L7YZEb$-*3>7RzSO2Pq z!dvV=>_Wchs<3a@;2`oJ;R3AvaQJchRqR7Pb9Hb!8uAZuA=Y_>4xLg3%weCw7L`D_5EM+ zH9j&?(U15N59EJ6ybE??ee$Q!v%1XI@ByR`b|AkDA4un)VDV=1livuTJ%QF!e@7VD zPv{!xw=?|9$2OcvzSx_t3HIL#5*sNf{#Hin|Ht#Y=-Y4MuCQGmLXXxbqT8_``H#_U zcpPhEwzoqE8=>Ws(UWo+dho15x7}0dx$-uq{{H6^5>c7L$xb=VGTq(CdBe9G8O7w*7|3Nrc zW`4l_cdq|OK~4My+hd^*!#*E?c5o-Qzz?ti<~tC!YkM?*2YTO|(GRf^`D5tfD|Rpp zU^dnzzXKcNA4w9!NYpqK{+Vq#wkDtBqj2PQMh}XeF@N>P;p6!mSc~%Wcr{l0BrLw^ z*pmE<=ps9VMKJ5(@FBMZIz^4pMV!2vgb^4N3nrthbRjx-YtaUFq921)HL;>xpRSAHeiCt@Pebb~cr1heQWE?BG7_%NQfP&Z zqMguPFev8dqucSmnBR#{l0S$>V#ZhD{(I2|Uc)B19X-hY#;REJ>x|T2LJh(uw4Yc` zBDJ{C8}fY%9X#FE%GhIHzynG(3#%f^NSC$DrTW7orE$ z$7pD?oemu>jpqBs{8aSI>_%LP@1avO__uJ*j7Jys0ki|jKS?+j*?tdmcLlZ~UlyIa z(fAeKjxVIK_|EXB7g+3UM(TfN|IE2?^fvi3{7J?ucnSA+_$zGFUT6nKqbJ|Z=x%ui znTllMZ4#b%KSV449YQn#?Z5+Qh+jc_{&~zN{t4w}(U5oee`oIjUPTqPZJ#r9lF$PQ zBoH8k-g{GuRO!;2bOZ!MdY5wOT{;ZCDZQ64p#@Zsrf?#nNN*x2ARtYpeD|7J!RY&5 z*Z+Ut(#^B?D$iPb&z^Eh60jLK5R~)$Bq%RnKK$Fg>&*w{tk?hw?>-0pJV4Nq3ZH>; z-uhm+Z>c5+<;vF%tO2eE-vS?iLX`idyEn>#GX4;h>&ReG63+l-B@Tjeh);krkt?9w zhRs*-k>3+ZM;;a%g0g!@f^s2R4a%;zK{>`3LD{{}K{>`zuidz^psY+6#i^hq+78N- z(pgXrX*AQ5iF5*G1vY?waXdyxUJ~5}WhRE##c)toCKo6(DGo|t1yE+%O6~m={i>e^ z%IW#J;$cwk0XIR}{izI>w~XQQJBn&3#LyFz8C#&-eD;G<*ac8d&xfGg8*-U0wgbg) z0hj}{K`HD!D7*U+C@bdmxtJ3a`@5j5Xj`A(o#{{%@?vocC}+bQ#SLIZ#$SM8;A>Eb z!-9OyF3t{0;pITNPILxkrZW}eKshv5bsQY*a}IS8P*$>ypH2}vpMb@{^`I=>dDZ^~ zN&)#pe9nEnE9m%vQs{Y53VH}iB2O~6fIOhgyuM-|Fcah1pd{V|%Axk3(9Q+09E!W3 z?1{X|ea`N$3)W*i9~Au!P-YmC!o{MXBx<2J6qG%)7?eY|4wNO22j#r~0h9uM2bm~8 z|C`cP6b5A}>w>}HKu`jQg9X63pd5mOpzM+FK$*caP)@I8sa$rK$+oDP~xwG za!8(l5|=rRnZt{CZ(-ESzpj5a^4B&TQKJYmxyFYuVJL9IH_;mqguZ#st zfuDiGa}Jc1{23IU(6nv=r9ttp49ec=0QzN#2h)*bI0KYTvK^EqKM58BpMtVvIn%j& zpb{vFI)Snm`happTL8*TH-T~pZBPzTJSaD{)1d63UqOjW6UO;3oibsrp`GFwP?m5F zD93!Cj!!Ax0HxsPpv*8T-1V;vO2THKB>WH*`)E)SuLWf#PlCn3SK*xh;#f4jdyMOV za%?+*Lev|Sr5dGU3zU^ur{lw*_@4!3$!~(PXVPSFFHF&j6+yYav;k%1x_~LcettS~ zQyB@$O4wi-@Cqo75fSc^=2R>K${~6il){^UQcy2Y4&@Y33RK0nb{psR>T|S?*25OY_dY2_>}}@W;GSN zgR)XnL0Rh6pzv&0JPziP=l^STVlV_}c9$|QC>NM&pqysiKna|n_GO@K%Dtf6hQ9^n z+W#w92#m<$&b%5ZaXmm;p|POsfz6<7?%jgq_ncJ2RZx!I9Z;6~IVg!TW_34XK~QE? z0TjaKp!kmhWs}a(@fuK0-&j!g##K;O=7rj$qg{Pru&2zhHXR}As)j*|vq71`7Emss zUxPw?50pYOW^-p;0+iFLJt#c0K{zw^d*oC!*T#b5z&9Vo&M>AL>`q z9VEPuQ|IJ-9|~i3h|)_zNkm?SPJqyx(pApI??aN;A+`h#!)0Mif#o3#*v^Vej=ZNLm*2DMk+~1ipYa5 zyH2LN>*_hJ_8TNUi~oFl4hWB~N`8Lhy{8`JiSjc6`*6ObGcN;iNdoQ?xDma`22D~F z9OSCr{JUDnkz|_S1GxXdZ#5Gs3wLt(K6PC@L&@=RGLFB+!+4j*8%mxY7)KDa zl7xG8wriO|297&#qj)0m%Shrzv|13$2SR<(C0AcbHb(wI;?albe(9ly!;4OYAHkCS z&oPDcJe8#aSCQy#3TOh^Sc+(-&Y2Xw==-8CiJx3t9zZ%ZNn+u*E3{f9eu(jR`oGi9ONDQQS(aun9NlVsu7Im_X?tS}M<)_OVINS? zMEV8s34^B|xr2u;-wHx)r9HrErQPt zND}27ouU+86^?53^E0vi`22_uj~Sj#%447}NRBo5HkD+9Aq_$~h6JDB%rA_0xFOeiI<3`{o=qlpFADZ#Zpx+--uE3r)BoyI( z<5`4mnDT#yUk1h>!?}av5b~2yq!!9$1jsG;KE|C&a*Ck&G?9aiRaI^IN#5denV_Woh=bDW-O z;CC#q$TG;LgZt49$G0}PmT^{Mwrd=}5y4XzeH6YTsW{&&GET)f56S)XMSR5P)R<^u zMDhotn8|W=N6`g^p(MA!i`Y8S(qn5pC*9%jIWdQQ{$|W8JyhReiKSH~MK&1DXvzG9 zs3XtN?}p`LFe4?*aMd0Q-;uNu#EJ~X-iLLmBfem7#u51c0e(gkxlP+c&Nlpvv}XiP zk7y!QP|k-eQC<>|nF1ov?<7GlolbomT58NlCU6YfFp}_4=_y0}VQfd#_cLOb;{PS~ z+1Pl^?7YeT5bk%q&Kbk+pCU;I4LGGUZm-oAhI|si@icyv*|Qn)Zy^;Kk8Ln3RUH)B zOaD5pAHIn)gz;kRJxQKGjy;SwV(f#@Fh7RV>NyfaQ^@Or*U-%)$t#NBa}dvdO(?Ei z88@fZfPlaH=18=zh77((f0{yjs7-XQvA2b{0JfXzy9e}_V>xEfxlV!|piNtYt{>y8 zx*}gf^fiIiNGg&Z{bSl!BsqZpL~LCdcP7DYT{YK}4^Qy<4ZTQq`t9&<3{N_j+t1I7 zvJ^O6AmJ(cDZ#CpD3X9e5RRjOc_c}ct&A%%Zk$9HMciuiarn!x7&uF8U--U%|O3 zwh%jjBp2uphvNZ$ztURcvypLj`laM-&d+Qgp(sPeS0V1IWI>wjFKi-TLR^|)k-s3W zjs9;)L%6Pn-){2FS3bt#xnB0WA`oz}z`lqbeP5SQ1PP9Zoq zZ7MpEVe0&@+G8O65)yuUgr_%zKj2e~@k#aTsmUaExfYa-$qiI}Ie1rN+eoa)a9*)E zl2;u*V|16+i=d(CzNZbNjYQXO@hVv@E(mPE&9LkTg9qO zKtF-8$X?>|usR|a(Ko<8iI{hYSGC8#9fCwTf`iDXOGDU|xR zl0IZShkhTm{i<>4Aj?gEHunA`3DB03xH34M_BOOI5xr-OB+Y4K$6K?r1*)PVFmdp+B1mu99t(TmVz>n;4r#F@NT80XPkxR zqkv+x^Az|{uK)7qeg1}c6;9pg2OyL;KSioQum;;tkd_9!VJixXe1m^dDS&T5bYY-K z31U_=fjRWoldKBkLP-wCN%a0o5UwGq$d^?0l;CqR1Eeuz?_uM8JVy#+`xAmV#_4Fh zo#yFIOO0*{m?(R-z>BQLMf3~cxJ-Yk7Q7NX>K5W2|1?y21cz_IZ!jcd`9uby`_grE z#+N8iqysI1B)lu^nM7+Th2qOE`tVfKBTG0>K>^?*K*0DXH`?X~MJ+pVt|RUL=@_c(IQ_Uopu8w*iybvFeawK{SeG|z4#OFQqm5KY2mKS>g zd_|g}pMbp_tv9|U;h&683C3-M$j=|*awHUmNEV1!(SNP8%LUN}5`9PTQS@60{;$Mp zvKomo_$?ylKH~(Oem3MJWpe-k9 zC5pNHredCull0q@WFdq@Nc6cR)s>hF_DwQl3;vAoiOAcYtBLuQvA->YS&&>wQt3;^ z`Ef`CZX(%-^jG6N7!*0GNotb-7tdD4^+~>kaT9!wF`h!)BHA`5f>Rm%RpU?M7f*|l zzY)6xh8$`T-%AwI4r5I!PQccmF<->UIZgleF>365ux+-}!$#Lv)=u0F? z`X$AQj7g$<8=n>=YlA+6>PM5Kr~1l0Cl&S;`1xC-+)fc9rBHSy$qzVm2Y+EHpW##; z`!Q_A87I>st5Dn@=w_qq4T_YZ{RW@GS@8w-LG%Y=KTJ#!3V6nNECn6MFF=dn9c#}j zl8wMg(DvZe||NYr#CH8u4_QzSnxW*qe=7* z{W&D(GjvZ7I+1BiBAR|Supq4tE3gWET9SN(Z%17X@eNh(3;3T)LH@5WHbI%5O*0#1 zKHW@nAxT9Nk-s2Kl!e&$LONWDzM_AOSq;XaHj^;*kno{`BWuB%+V|sokhtIQtL<2s ze@hf+7^HwW9S+;+-vt{Hv>9TNhPpao_nqWW1Jsdm7 zeoc;&u^OXJt^pQF)t%>W9ROEX_>8HcyW&8_#)fqQu z*0qQ=(HW9U&ObiJ@_Ys9e64sUPKh##{s~$^Y)1)b0^uwCkC42tIyF;VkKcM$@HVTo z8r+ZHW^`ZZq$V>Kd4O#nJ}oH3|45CuA#Oqu@91nQV63SHWh1Z-<5}49({_`506G&Q zks)woz|J3Kb)+IbAEV1cd?Y@}G_e>vCh4b?<1eySliqDp?qgdHAje=YzQ^?uxwsBhmxs)VdPC zYS2)KLU25%$*=2d`)LA;xCX@3r&+|T1J6>_6P8@0DREzcXW;k=TU*Ahz-r3B9NRVd zC_y9(1=s|3V*HUV``ZMH?A6scr+U$aa-CSG1<6>%RAP6;=7Y1F7PFGrL-gkplM{@l zn7sJ=vp^*BJBhPlsD#5|`Zmdrg2f==cVRkmm4DyGegxeXYy*-UrZMUxA8~&QaZzTP z9KXZ(JaD4nQS63(3HB$+^!SG|C{6o{Dld~jkBZ~|36*XpAXsN7KUCKUzXIsfg4=b5 zGqGl#9d)* ze}TTU&Lo@elPma~AVG036SMt={sj8pk?15zit7qQXfb?E!ee9KqWbs1Qd;1?#N*$K zz;|)FNmbc#IIG2+(dzkil%C`0?=j0CDe4R@jPWFr{6@@O+Mkeqt8q{{@(FRF#JrFG ztR^*yIl%aLIGmjpiD3?dy*ks@1QaCkRT79Cbm7gvA7U5T1W`CdS@AnTe5|YX@Ev;3 zYxFNEss`MvbrRFS{peeP2hnGS=bF6#Ta~sSvSTP~Lbei|No(a)$YV6RQ;_x|I03!L zbg(0FA}#2jr}#hd>%;gn#<@uH0WmGm@1o71eUAPrc`D#1M{^@6`_GZ7IHbmyfvTQE zJe~1MNOChCMIm29HWh--*!I%jPWv2vHi}A=^Ndr$F`3vix=La@hwdJ@o`heZKLt-X z+~ef=uQASt80^O(JLJt6^HTtxw3PT5$8Q*a3yK_2((g$60m2#E=Mtm@&mS?DPnAr*@I5d zk=QB{n_7eVYVlX`Nv2$#$yZtWiR=F@{iaD0z1HB$iZ39~$m~Quq#a`X8;ROcP@)|E zPf^A3DTQti$hVvv8Ls{xXpny%4ufdF(pIAUmdfhk^cMk>DQGb*fT6&Fr!#z`jwJbW1J|}@mWgjMO74Lypi!|x~ihSAN*$iJOyDaK=L6N)=-Q{8wd-~ zUrW1+{RRb$f+!AKJcV4-gheT)JY$iUa16v(qzB1z;-8xM@#@-mb7g40eSfr&cv-In0qAR)@dvq+e zvGAr~{GAr~EjSqyb*lLREgzJNk5lIOKN|0U3= z2XQ5oV<9Mt^LBzpLU<6ohkY0Nr1Ca18j7y6CQ7trrZXK|Em~#lsX2AGrq5|2E}Qa30McFME@)JKGmKj@HNE*YqI(jTLMCn6X^PZ)$tM856YWb z4Rw`LFsT>1ni}Hyl$a{6`<%$bspWeJ&UX<$#P1X3X{#}B!?Oa8Z^)mMxDmwh>#se( zQ^P6REo>sAuw7)*A2JSN4vQEU#U83WJrupzhvL5k->TqpxQfG_T7F*jA%hmehTPKt z>2ILrg|G`jxj~UXX+J==K^BXIA{DWj+Aj)MH8_q!E;53CU5(uZMx*-$pMJFZaB$bk za5z-%2Zb4x1=Hx%4uIKMmOiwyu>D4K7*iX{(rm*vg?@nc6EPn#E=zlfZ5|n89A-8l zSsqipNKWjdX(Cnd8$}cOjpkpahUXAALU9C=rUX}IEHX$5&uCC){QE)tDL#GZKUe)X z%99oUArxH)yGVN`F$B&hph!9V7Z5+$T_(S$q!Ng68jeZjE!ec3_23%}X-V|48g2T8)Zr$5 zFGQOlG$^jP`lMx?3|moj1~XZKPom7AUlw}@^am)QG|gZv@;j{!lb&c>zN58a=v%~iZ zF}W$MrF3JSA`nq?fdy zycBqtf=49UHQ`0b^P%sIy&1mq;rI#sRXNkb`+nm4fe&!%g7aCFCBQnmDv~^w!U|y* z=}cP(!3N0gYobv4S2X!S`pGrHM0gVAL-D8Zsv7etg{*g0isL^Y$2K^KT)<&G{rBlF z#%Y@dh%J&LK2g6vNw`lFOxNPXX9dZlbzELoRP-jk?-Kh;`9^7xzssZ3CE75YPAOq? z2tr8EpMcx6a}+Y175M<&mr?*E{ek=VogwBp_8Ccvc*b~&Gdo`1<3Ex@iX|!L7j%)j zV*cv7)PGRXJCL+aLPQSFWhIjSeMnk?vk2ZxlG9+lBr7C7Pc+duY|TZHL=Mo;LY^UT zW_9!W`T2BqEzaN4yj1oPPSYUyJ4wZlb!FO;>?hT)!*>DtSPJ=^*vi<_LS6xE1NmBw z8O1o99L;p1QqMSIZom^~y4N3m8lTzVDDoA=lijZKZ$tDcApV8X76L``pnC^hKP{#$ z*o-3PlH@k6KP`;Qw?Pr*(TjXde+YRN5f`VcdRaaXDn`OzwAu#{`AO-z75G>YU$$5!>Jos2wpbGYT`0l1>O*sD;? zQt+kx&B*<9o-@;<5G}^A96r1~Ey+U!Q`|y>-wnX(m%Ja`P6dh31g`h3LiE@mj z%}I2HprkSaeO1V2Xp-ji`;b875xOm^cQx+6@l3KB`myl+TR8qT3EqM6wvv=2L2Clm zvV=A1-@!JQwuJF)ifIF$LI0B$I~kk2t&9b7m^)>;4luQ2_8>j`Ls}RZApJB_5t{nB*_o> zO~R)E`ePIl5B9`wCicuEu9ze?1NM37>bZscJ^M-ckl>jpM{1RI!7b=Sozi=vokyh6IDbvEWJyOO&M)F_@T{`2RrM6DHO|eu88$q{%T9BWMln zD2^t$n|2P{hd7s~eMvtj#SVw`68_~F7pDD;PNcmHo^Od?kFF4YrQn!~?_>N#{v`P* zFd4Rd=pOj#WTw&>orO55Y}Y;Uzv%bHn9!yn~YBo{q`jJ zkbXDv>;k*OIS{_ziBG}U-x4R06evP*eud)z2sWV$L-!Re3P%x>qUPaWhH(fao$1d- zKbqAM$)g39!LI>Lq&*31!{Gy;!5yF_O1OLYFKH1lWz3IQUM`*65P=q@NMq!^BsH?>qGC>A#}r8uAllb?A)K%0Ghq zH^vJfe-Eb{X;OR3?e=Q3F@KD#Du(zMRtRGz~9iFh9e8S zZRxM`)44vt(kSD846X5+pgmJp{ckP7sztcZ}foam>Z|J#0@H?*Idg zZ$grrM71CvK>LHZG2rVIJLliao+>2Y$6dO1>@;-e_CNE6*^g3s$JVuvxA5+0Yi*15 zX0{f@dNW#sV!b)+ld;~7>FtRpypyAY`*-QoyH{sB;*R&-sHzF01Mz|Ffw+WGfo*}< zgfTG*qZ7siw$b7eCZZS}*c*ro#Kr`6VLuQ!kT5k6TcK#D-krO=6BF3ZXrDdAH1e70 zR^dDYHbfT{;<-A7&)xifo2A)Mjaz8drV*-9@_))F@e3L!gEi;n1r!~ z-sr7SAx19y-DF1YGmQS*#loMx)dV0&@a01G8ug1FP)Pk;Y`hYE;S0l7b>*DIhj*z&e%1h)9+&f&RgS zX;$$VBixRNG8zV14Wf-8>&av@%pRJ}nBtYd%+}y+MwnGU+Q@E=nPP@ngR&VJtZvzj zRsS%25e-9$9ELB*yUi|}*GTL2uCVw`gpp~Snh(g5CQM71VfDyoOi%vL@bn!c!ivt% zLS-||VC&b{M*4!386U7&-<`~JD$|Y)?1{-YtY^m|-Fpt~*}ikXj)O-8=I1Zs3Aicj z0r`!V-n21Hd2Y;G<%*Y!DO#q~m;0=Sh2Xwl!1y&?OkgLJyUNL-oFy*ym4e1x)1aP# zMUDQcYxf$|rPqMo{f5>a(50ufyr@ymu2#(GYlQ5Ou)sF^NC~4th&7X6gJm_WWM;L( zE}H4>eC3RahCQ=_G0?QUOO2S6F&xt!Y@hgqQFf}zMrk7r+vHPr5i9P{)T(ND4ZCG^ zW0K+BYhSBjR7z$9;;e@!jP%yQ21ZaA!T;Gxc9jOkE;EMhnJ`VZSYh_iZph+fAFwAU zkQ24_u)Yy#muO-XG}5~J+SxjekJY=G5t;iS%Nob-VO_V=b#^qf+s|%v25j6wto5*| zk;!V<%*boqYi6X$_5UkoWn&|wU8A|NASgu2F9rw?xh%Sq?cZ62^m4xU7Yq zRd~y>waT|KY9)r+)7ls}yjG^^MzlSnoiW8s?e0=>^aK`J3saj}?ZzJ%SG`u{&PF?{ zPZy(-eWA0l+^}|BHnU~r%#EXjePm;*3FBi3l+}_gH^J)D%@}OQ@J}*pR39Tln4F5t zp9-Bt;{>(8=wtkrGL%)?6T|FdsW~pN-`+Fan32pHGTumK<+*4?S_{Xsw>}zgjLI2H zu6?ZFZnwg9Y#~TAv|0~X_^1YeKUN^z0>nr(Ll9Qfk zcmlI2M%Invqig3BW;xkTD zCNmIQ#PXdn^V&@o7*oSjC5)r&Jx+-`tV%i)1JoN;3V2l;PXOpRKN8^+{wSgpV& z<5j6eva#80By_JtLcLqIkEdDVdW9j|C50u7mMfa<%`sdF+te_l*yDDIEyi)vlu|}b zG1Gfv?c_U*#opA*ok_?cI>5K**YCTklbRbgQvtcyFjs3nZEyY4bd zc~b;d2Id7;1U3d1+RI{%dcoFT`;1Wg`934RS2jgdYB}$9YX;)25(kV<$+*DGki%px zJYamEoZY!Cu-#L;x|J@@$Z8LXH>RZso$V}*99P*qGpq*{jR@}^tJHC0ecFUi*}HLa zYJ=P+>^sMeC5B~wYt*uje`91aqXTSz+3>8VY=60?F-cjtqP3e=*&Q%`OlI{yYXsXP ze=@%E=E6-5l-AB;wOPG-`Jz+BSvWRg3n`0;$oGcuWI=75QZ#ny)o0>DN#M@k< zmjE7U@n_rX9b>H-336*lE4K$DMuoeWO~?K1!Zp6~1re zb4_-e`$o}V?-J`mRU=byY)lnT;7z}(C$QM6QqIV1ovv;sx4wI9)K*6$Hpa^L#AvI- zl(8|xKJMMA1id1{iSl)J`$ zt}Vk`aFcldDQBf~n%jM!8RrZuJkH1*;+*g7l-7S4&AHX?_{+%ev$nl7f|5LY^nGPy zw$r{ghIrpRd}Oe8UN)m6n%ApRU#=OPIC|+I;~q-_*<({YyuGaMIJbAv%W`@{NG3JpV%h$mS3#rw# zDff&fR^>9>&U|^yKu~aCd0=H=k^N&qb5cm;KZjcie)C9R)sHYE?BONNTc&gPtnp>d z-1h9U<{w_G(`KG)C%`55Qql$r^V zisSi5M(D*p)xGD-eVa{huPtwuHd4rG>Rce=od-wzNky|xQ0iI#y24hkX1*}&1~ts_ zh7nk9JyIwsruTlIn%yMIKUUI~6!QRHbPabP{fa5%ICL@oUl0>Gw zLtY1Dw0xO;>E<^ugRGPv5B>(0rLs=JxT2%9Avi=)LMlZPap zm#mIGIL0k{n05vZzdVPJPM99U)lTlm2Y3y3pobZ0RruJPlZsW9yO$-aZ=d}8J8TplLhx6FsZ*7{{;bF1nyv%UR#nYlk}gg&J0mAstCdhfT7>^JX* zNR^S+s>5b>>*vGfb|VmPA39>D^aeL$K@-MW6&D*(*3+-e%<0%#>@gN!p7ePPEn;0+ z%sr~fd?Tk-f1R1mDt^>#Z#A9I>+8f}b^C{-W>3S~lFygfu6Dwl1Q)VFQNtvFe@Ty@5Z^8Y!(6r_7q6)Iy~5 z!cty&S@-7gLL~CEc{@cKE*0(r+A>db9)_+}uPr%snXr2ae(;!5Zv9~nuqNi=YLKRt5o{HEY(`s;9`k0wfX8M&Ba~C$xsp%P z({1%r^YXuM;Ia0MXJ$XI39q&CGBX^LmxFgb1NG%FxT)6p(_HCI`R0xwZ{Feaxt-cCiAuSrXya~R$krgN|@^5kw5JQqlBG0 zg>SDB%4-gm?af8XDwf)pF)f$DB-(#oP~}YR>t`A1j4-2$$MUA*=tl^b)1CX%6?U>261EC7%C|1Ge?xHjj_N zX??T2MVN-XXyTqKS1BgOzS5?1On4tqA5K{fZppHzoQuXAy_F`?w;{c(=|Nn$tWt(t ztoM7Ym2dN$bUo6S5RzPC{racked_instance_count} " @@ -4771,7 +4769,7 @@ msgstr "" "href=\"{url}\">{racked_instance_count} экземпляр(ов) уже смонтированых в" " стойках." -#: dcim/models/devices.py:330 +#: dcim/models/devices.py:331 msgid "" "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." @@ -4779,157 +4777,157 @@ msgstr "" "Необходимо удалить все шаблоны отсеков устройств, связанные с этим " "устройством, прежде чем рассекретить его как родительское устройство." -#: dcim/models/devices.py:336 +#: dcim/models/devices.py:337 msgid "Child device types must be 0U." msgstr "Типы дочерних устройств должны быть 0U." -#: dcim/models/devices.py:404 +#: dcim/models/devices.py:405 msgid "module type" msgstr "тип модуля" -#: dcim/models/devices.py:405 +#: dcim/models/devices.py:406 msgid "module types" msgstr "типы модулей" -#: dcim/models/devices.py:473 +#: dcim/models/devices.py:475 msgid "Virtual machines may be assigned to this role" msgstr "Эта роль может быть назначена виртуальным машинам." -#: dcim/models/devices.py:485 +#: dcim/models/devices.py:487 msgid "device role" msgstr "роль устройства" -#: dcim/models/devices.py:486 +#: dcim/models/devices.py:488 msgid "device roles" msgstr "роли устройств" -#: dcim/models/devices.py:503 +#: dcim/models/devices.py:505 msgid "Optionally limit this platform to devices of a certain manufacturer" msgstr "" "Опционально ограничьте эту платформу устройствами определенного " "производителя" -#: dcim/models/devices.py:515 +#: dcim/models/devices.py:517 msgid "platform" -msgstr "платформы" - -#: dcim/models/devices.py:516 -msgid "platforms" msgstr "платформ" -#: dcim/models/devices.py:564 +#: dcim/models/devices.py:518 +msgid "platforms" +msgstr "платформы" + +#: dcim/models/devices.py:566 msgid "The function this device serves" msgstr "Функция, которую выполняет это устройство" -#: dcim/models/devices.py:596 +#: dcim/models/devices.py:598 msgid "Chassis serial number, assigned by the manufacturer" msgstr "Серийный номер корпуса, присвоенный производителем" -#: dcim/models/devices.py:604 dcim/models/devices.py:1181 +#: dcim/models/devices.py:606 dcim/models/devices.py:1186 msgid "A unique tag used to identify this device" msgstr "Уникальный тег, используемый для идентификации этого устройства" -#: dcim/models/devices.py:631 +#: dcim/models/devices.py:633 msgid "position (U)" msgstr "положение (U)" -#: dcim/models/devices.py:638 +#: dcim/models/devices.py:640 msgid "rack face" msgstr "лицевая сторона стойки" -#: dcim/models/devices.py:658 dcim/models/devices.py:1390 +#: dcim/models/devices.py:660 dcim/models/devices.py:1395 #: virtualization/models/virtualmachines.py:98 msgid "primary IPv4" msgstr "основной IPv4" -#: dcim/models/devices.py:666 dcim/models/devices.py:1398 +#: dcim/models/devices.py:668 dcim/models/devices.py:1403 #: virtualization/models/virtualmachines.py:106 msgid "primary IPv6" msgstr "основной IPv6" -#: dcim/models/devices.py:674 +#: dcim/models/devices.py:676 msgid "out-of-band IP" msgstr "внеполосный IP-адрес" -#: dcim/models/devices.py:691 +#: dcim/models/devices.py:693 msgid "VC position" msgstr "Позиция VC" -#: dcim/models/devices.py:695 +#: dcim/models/devices.py:697 msgid "Virtual chassis position" msgstr "Положение виртуального шасси" -#: dcim/models/devices.py:698 +#: dcim/models/devices.py:700 msgid "VC priority" msgstr "Приоритет VC" -#: dcim/models/devices.py:702 +#: dcim/models/devices.py:704 msgid "Virtual chassis master election priority" msgstr "Приоритет выбора основного виртуального шасси" -#: dcim/models/devices.py:705 dcim/models/sites.py:207 +#: dcim/models/devices.py:707 dcim/models/sites.py:207 msgid "latitude" msgstr "широта" -#: dcim/models/devices.py:710 dcim/models/devices.py:718 +#: dcim/models/devices.py:712 dcim/models/devices.py:720 #: dcim/models/sites.py:212 dcim/models/sites.py:220 msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgstr "Координата GPS в десятичном формате (xx.yyyyyy)" -#: dcim/models/devices.py:713 dcim/models/sites.py:215 +#: dcim/models/devices.py:715 dcim/models/sites.py:215 msgid "longitude" msgstr "долгота" -#: dcim/models/devices.py:786 +#: dcim/models/devices.py:788 msgid "Device name must be unique per site." msgstr "Имя устройства должно быть уникальным для каждого сайта." -#: dcim/models/devices.py:797 ipam/models/services.py:75 +#: dcim/models/devices.py:799 ipam/models/services.py:75 msgid "device" msgstr "устройство" -#: dcim/models/devices.py:798 +#: dcim/models/devices.py:800 msgid "devices" -msgstr "приборы" +msgstr "устройства" -#: dcim/models/devices.py:838 +#: dcim/models/devices.py:840 #, python-brace-format msgid "Rack {rack} does not belong to site {site}." msgstr "Стойка {rack} не принадлежит сайту {site}." -#: dcim/models/devices.py:843 +#: dcim/models/devices.py:845 #, python-brace-format msgid "Location {location} does not belong to site {site}." -msgstr "Местоположение {location} не принадлежит сайту {site}." +msgstr "Локация {location} не принадлежит сайту {site}." -#: dcim/models/devices.py:849 +#: dcim/models/devices.py:851 #, python-brace-format msgid "Rack {rack} does not belong to location {location}." -msgstr "Стойка {rack} не принадлежит расположению {location}." +msgstr "Стойка {rack} не принадлежит локации {location}." -#: dcim/models/devices.py:856 +#: dcim/models/devices.py:858 msgid "Cannot select a rack face without assigning a rack." msgstr "Невозможно выбрать лицевую сторону стойки, не выбрав саму стойку." -#: dcim/models/devices.py:860 +#: dcim/models/devices.py:862 msgid "Cannot select a rack position without assigning a rack." msgstr "Невозможно выбрать позицию в стойке, не выбрав саму стойку." -#: dcim/models/devices.py:866 +#: dcim/models/devices.py:868 msgid "Position must be in increments of 0.5 rack units." msgstr "Позиция должна быть указана с шагом 0,5 единицы стойки." -#: dcim/models/devices.py:870 +#: dcim/models/devices.py:872 msgid "Must specify rack face when defining rack position." msgstr "При определении лицевой стороны необходимо указать позицию в стойке." -#: dcim/models/devices.py:878 +#: dcim/models/devices.py:880 #, python-brace-format msgid "" "A 0U device type ({device_type}) cannot be assigned to a rack position." msgstr "Тип устройства 0U ({device_type}) не может быть отнесено к стойке." -#: dcim/models/devices.py:889 +#: dcim/models/devices.py:891 msgid "" "Child device types cannot be assigned to a rack face. This is an attribute " "of the parent device." @@ -4937,15 +4935,15 @@ msgstr "" "Устройствам с указанным в типе свойством \"дочернее\" нельзя выбрать лицевую" " сторону стойки. Этот атрибут указывается для \"родительского\" устройства." -#: dcim/models/devices.py:896 +#: dcim/models/devices.py:898 msgid "" "Child device types cannot be assigned to a rack position. This is an " "attribute of the parent device." msgstr "" -"Типы детских устройств нельзя отнести к позиции в стойке. Это атрибут " +"Типы дочерних устройств нельзя отнести к позиции в стойке. Это атрибут " "родительского устройства." -#: dcim/models/devices.py:910 +#: dcim/models/devices.py:912 #, python-brace-format msgid "" "U{position} is already occupied or does not have sufficient space to " @@ -4954,22 +4952,22 @@ msgstr "" "U{position} уже занят или в нем недостаточно места для размещения этого типа" " устройств: {device_type} ({u_height}U)" -#: dcim/models/devices.py:925 +#: dcim/models/devices.py:927 #, python-brace-format msgid "{ip} is not an IPv4 address." msgstr "{ip} не является адресом IPv4." -#: dcim/models/devices.py:934 dcim/models/devices.py:949 +#: dcim/models/devices.py:936 dcim/models/devices.py:951 #, python-brace-format msgid "The specified IP address ({ip}) is not assigned to this device." msgstr "Указанный IP-адрес ({ip}) не назначено этому устройству." -#: dcim/models/devices.py:940 +#: dcim/models/devices.py:942 #, python-brace-format msgid "{ip} is not an IPv6 address." msgstr "{ip} не является адресом IPv6." -#: dcim/models/devices.py:967 +#: dcim/models/devices.py:969 #, python-brace-format msgid "" "The assigned platform is limited to {platform_manufacturer} device types, " @@ -4978,26 +4976,26 @@ msgstr "" "Назначенная платформа ограничена {platform_manufacturer} типы устройств, но " "данный тип устройства относится к {devicetype_manufacturer}." -#: dcim/models/devices.py:978 +#: dcim/models/devices.py:980 #, python-brace-format msgid "The assigned cluster belongs to a different site ({site})" msgstr "Назначенный кластер принадлежит другому сайту ({site})" -#: dcim/models/devices.py:986 +#: dcim/models/devices.py:988 msgid "A device assigned to a virtual chassis must have its position defined." msgstr "" "Положение устройства, назначенного виртуальному шасси, должно быть " "определено." -#: dcim/models/devices.py:1188 +#: dcim/models/devices.py:1193 msgid "module" msgstr "модуль" -#: dcim/models/devices.py:1189 +#: dcim/models/devices.py:1194 msgid "modules" msgstr "модули" -#: dcim/models/devices.py:1205 +#: dcim/models/devices.py:1210 #, python-brace-format msgid "" "Module must be installed within a module bay belonging to the assigned " @@ -5006,21 +5004,21 @@ msgstr "" "Модуль должен быть установлен в модульном отсеке, принадлежащем назначенному" " устройству ({device})." -#: dcim/models/devices.py:1309 +#: dcim/models/devices.py:1314 msgid "domain" msgstr "Домен" -#: dcim/models/devices.py:1322 dcim/models/devices.py:1323 +#: dcim/models/devices.py:1327 dcim/models/devices.py:1328 msgid "virtual chassis" msgstr "виртуальное шасси" -#: dcim/models/devices.py:1338 +#: dcim/models/devices.py:1343 #, python-brace-format msgid "" "The selected master ({master}) is not assigned to this virtual chassis." msgstr "Выбранный мастер ({master}) не назначено этому виртуальному шасси." -#: dcim/models/devices.py:1354 +#: dcim/models/devices.py:1359 #, python-brace-format msgid "" "Unable to delete virtual chassis {self}. There are member interfaces which " @@ -5029,33 +5027,33 @@ msgstr "" "Невозможно удалить виртуальное шасси {self}. Существуют интерфейсы-члены, " "которые образуют межкорпусные интерфейсы LAG." -#: dcim/models/devices.py:1379 vpn/models/l2vpn.py:37 +#: dcim/models/devices.py:1384 vpn/models/l2vpn.py:37 msgid "identifier" msgstr "идентификатор" -#: dcim/models/devices.py:1380 +#: dcim/models/devices.py:1385 msgid "Numeric identifier unique to the parent device" msgstr "Цифровой идентификатор, уникальный для родительского устройства" -#: dcim/models/devices.py:1408 extras/models/models.py:129 +#: dcim/models/devices.py:1413 extras/models/models.py:129 #: extras/models/models.py:724 netbox/models/__init__.py:114 msgid "comments" msgstr "комментарии" -#: dcim/models/devices.py:1424 +#: dcim/models/devices.py:1429 msgid "virtual device context" msgstr "виртуальный контекст" -#: dcim/models/devices.py:1425 +#: dcim/models/devices.py:1430 msgid "virtual device contexts" msgstr "виртуальные контексты" -#: dcim/models/devices.py:1457 +#: dcim/models/devices.py:1462 #, python-brace-format msgid "{ip} is not an IPv{family} address." msgstr "{ip} не является IPV{family} адрес." -#: dcim/models/devices.py:1463 +#: dcim/models/devices.py:1468 msgid "Primary IP address must belong to an interface on the assigned device." msgstr "" "Основной IP-адрес должен принадлежать интерфейсу на назначенном устройстве." @@ -5087,8 +5085,7 @@ msgstr "панели питания" msgid "" "Location {location} ({location_site}) is in a different site than {site}" msgstr "" -"Местоположение {location} ({location_site}) находится на другом сайте, чем " -"{site}" +"Локация{location} ({location_site}) находится на другом сайте, чем {site}" #: dcim/models/power.py:107 msgid "supply" @@ -5271,8 +5268,8 @@ msgid "" "Rack unit numbering must begin at {position} or less to house currently " "installed devices." msgstr "" -"Нумерация стеллажей должна начинаться с {position} или меньше для размещения" -" установленных в настоящее время устройств." +"Нумерация стоек должна начинаться с {position} или меньше для размещения " +"установленных в настоящее время устройств." #: dcim/models/racks.py:269 #, python-brace-format @@ -5281,7 +5278,7 @@ msgstr "Локация должна быть с того же места, {site} #: dcim/models/racks.py:522 msgid "units" -msgstr "единиц" +msgstr "юниты" #: dcim/models/racks.py:548 msgid "rack reservation" @@ -5300,7 +5297,7 @@ msgstr "" #: dcim/models/racks.py:579 #, python-brace-format msgid "The following units have already been reserved: {unit_list}" -msgstr "Следующие номера уже зарезервированы: {unit_list}" +msgstr "Следующие юниты уже зарезервированы: {unit_list}" #: dcim/models/sites.py:49 msgid "A top-level region with this name already exists." @@ -5368,7 +5365,7 @@ msgstr "место" #: dcim/models/sites.py:239 msgid "sites" -msgstr "места" +msgstr "Сайты" #: dcim/models/sites.py:303 msgid "A location with this name already exists within the specified site." @@ -5390,16 +5387,15 @@ msgstr "локации" #, python-brace-format msgid "Parent location ({parent}) must belong to the same site ({site})." msgstr "" -"Местонахождение родителя ({parent}) должен принадлежать тому же сайту " -"({site})." +"Локация родителя ({parent}) должен принадлежать тому же сайту ({site})." #: dcim/tables/cables.py:54 msgid "Termination A" -msgstr "Прекращение A" +msgstr "Окончание A" #: dcim/tables/cables.py:59 msgid "Termination B" -msgstr "Прекращение В" +msgstr "Окончание В" #: dcim/tables/cables.py:65 wireless/tables/wirelesslink.py:22 msgid "Device A" @@ -5411,19 +5407,19 @@ msgstr "Устройство B" #: dcim/tables/cables.py:77 msgid "Location A" -msgstr "Местоположение A" +msgstr "Локация A" #: dcim/tables/cables.py:83 msgid "Location B" -msgstr "Местоположение B" +msgstr "Локация B" #: dcim/tables/cables.py:89 msgid "Rack A" -msgstr "Стеллаж A" +msgstr "Стойка A" #: dcim/tables/cables.py:95 msgid "Rack B" -msgstr "Стеллаж B" +msgstr "Стойка B" #: dcim/tables/cables.py:101 msgid "Site A" @@ -5445,7 +5441,7 @@ msgstr "Консольный порт" msgid "Reachable" msgstr "Доступен" -#: dcim/tables/connections.py:46 dcim/tables/devices.py:529 +#: dcim/tables/connections.py:46 dcim/tables/devices.py:533 #: templates/dcim/inventoryitem_edit.html:64 #: templates/dcim/poweroutlet.html:47 templates/dcim/powerport.html:18 msgid "Power Port" @@ -5464,7 +5460,7 @@ msgstr "Устройства" msgid "VMs" msgstr "Виртуальные машины" -#: dcim/tables/devices.py:133 dcim/tables/devices.py:245 +#: dcim/tables/devices.py:133 dcim/tables/devices.py:249 #: extras/forms/model_forms.py:515 templates/dcim/device.html:114 #: templates/dcim/device/render_config.html:11 #: templates/dcim/device/render_config.html:15 @@ -5473,62 +5469,62 @@ msgstr "Виртуальные машины" #: templates/virtualization/virtualmachine.html:47 #: templates/virtualization/virtualmachine/render_config.html:11 #: templates/virtualization/virtualmachine/render_config.html:15 -#: virtualization/tables/virtualmachines.py:93 +#: virtualization/tables/virtualmachines.py:106 msgid "Config Template" msgstr "Шаблон конфигурации" -#: dcim/tables/devices.py:216 dcim/tables/devices.py:1074 +#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 #: ipam/forms/bulk_import.py:511 ipam/forms/model_forms.py:296 #: ipam/tables/ip.py:352 ipam/tables/ip.py:418 ipam/tables/ip.py:441 #: templates/ipam/ipaddress.html:12 templates/ipam/ipaddress_edit.html:14 -#: virtualization/tables/virtualmachines.py:81 +#: virtualization/tables/virtualmachines.py:94 msgid "IP Address" msgstr "IP-адрес" -#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 -#: virtualization/tables/virtualmachines.py:72 +#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 +#: virtualization/tables/virtualmachines.py:85 msgid "IPv4 Address" msgstr "Адрес IPv4" -#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 -#: virtualization/tables/virtualmachines.py:76 +#: dcim/tables/devices.py:228 dcim/tables/devices.py:1086 +#: virtualization/tables/virtualmachines.py:89 msgid "IPv6 Address" msgstr "Адрес IPv6" -#: dcim/tables/devices.py:239 +#: dcim/tables/devices.py:243 msgid "VC Position" msgstr "Позиция VC" -#: dcim/tables/devices.py:242 +#: dcim/tables/devices.py:246 msgid "VC Priority" msgstr "Приоритет VC" -#: dcim/tables/devices.py:249 templates/dcim/device_edit.html:38 +#: dcim/tables/devices.py:253 templates/dcim/device_edit.html:38 #: templates/dcim/devicebay_populate.html:16 msgid "Parent Device" msgstr "Родительское устройство" -#: dcim/tables/devices.py:254 +#: dcim/tables/devices.py:258 msgid "Position (Device Bay)" msgstr "Положение (отсек для устройств)" -#: dcim/tables/devices.py:263 +#: dcim/tables/devices.py:267 msgid "Console ports" msgstr "Консольные порты" -#: dcim/tables/devices.py:266 +#: dcim/tables/devices.py:270 msgid "Console server ports" msgstr "Порты консольного сервера" -#: dcim/tables/devices.py:269 +#: dcim/tables/devices.py:273 msgid "Power ports" msgstr "Порты питания" -#: dcim/tables/devices.py:272 +#: dcim/tables/devices.py:276 msgid "Power outlets" msgstr "Розетки питания" -#: dcim/tables/devices.py:275 dcim/tables/devices.py:1087 +#: dcim/tables/devices.py:279 dcim/tables/devices.py:1091 #: dcim/tables/devicetypes.py:125 dcim/views.py:1005 dcim/views.py:1244 #: dcim/views.py:1930 netbox/navigation/menu.py:82 #: netbox/navigation/menu.py:238 templates/dcim/device/base.html:37 @@ -5538,53 +5534,53 @@ msgstr "Розетки питания" #: templates/dcim/virtualdevicecontext.html:85 #: templates/virtualization/virtualmachine/base.html:27 #: templates/virtualization/virtualmachine_list.html:14 -#: virtualization/tables/virtualmachines.py:87 virtualization/views.py:368 +#: virtualization/tables/virtualmachines.py:100 virtualization/views.py:368 #: wireless/tables/wirelesslan.py:55 msgid "Interfaces" msgstr "Интерфейсы" -#: dcim/tables/devices.py:278 +#: dcim/tables/devices.py:282 msgid "Front ports" msgstr "Передние порты" -#: dcim/tables/devices.py:284 +#: dcim/tables/devices.py:288 msgid "Device bays" msgstr "Отсеки для устройств" -#: dcim/tables/devices.py:287 +#: dcim/tables/devices.py:291 msgid "Module bays" msgstr "Отсеки для модулей" -#: dcim/tables/devices.py:290 +#: dcim/tables/devices.py:294 msgid "Inventory items" msgstr "Комплектующие" -#: dcim/tables/devices.py:329 dcim/tables/modules.py:56 +#: dcim/tables/devices.py:333 dcim/tables/modules.py:56 #: templates/dcim/modulebay.html:17 msgid "Module Bay" msgstr "Модульный отсек" -#: dcim/tables/devices.py:350 +#: dcim/tables/devices.py:354 msgid "Cable Color" msgstr "Цвет кабеля" -#: dcim/tables/devices.py:356 +#: dcim/tables/devices.py:360 msgid "Link Peers" msgstr "Связать узлы" -#: dcim/tables/devices.py:359 +#: dcim/tables/devices.py:363 msgid "Mark Connected" msgstr "Отметить подключение" -#: dcim/tables/devices.py:475 +#: dcim/tables/devices.py:479 msgid "Maximum draw (W)" msgstr "Максимальная потребляемая мощность (Вт)" -#: dcim/tables/devices.py:478 +#: dcim/tables/devices.py:482 msgid "Allocated draw (W)" msgstr "Выделенная мощность (Вт)" -#: dcim/tables/devices.py:578 ipam/forms/model_forms.py:707 +#: dcim/tables/devices.py:582 ipam/forms/model_forms.py:711 #: ipam/tables/fhrp.py:28 ipam/views.py:597 ipam/views.py:691 #: netbox/navigation/menu.py:146 netbox/navigation/menu.py:148 #: templates/dcim/interface.html:351 templates/ipam/ipaddress_bulk_add.html:15 @@ -5593,12 +5589,12 @@ msgstr "Выделенная мощность (Вт)" msgid "IP Addresses" msgstr "IP-адреса" -#: dcim/tables/devices.py:584 netbox/navigation/menu.py:190 +#: dcim/tables/devices.py:588 netbox/navigation/menu.py:190 #: templates/ipam/inc/panels/fhrp_groups.html:5 msgid "FHRP Groups" msgstr "Группы FHRP" -#: dcim/tables/devices.py:596 templates/dcim/interface.html:90 +#: dcim/tables/devices.py:600 templates/dcim/interface.html:90 #: templates/virtualization/vminterface.html:70 templates/vpn/tunnel.html:18 #: templates/vpn/tunneltermination.html:14 vpn/forms/bulk_edit.py:75 #: vpn/forms/bulk_import.py:76 vpn/forms/filtersets.py:41 @@ -5607,20 +5603,20 @@ msgstr "Группы FHRP" msgid "Tunnel" msgstr "Туннель" -#: dcim/tables/devices.py:621 dcim/tables/devicetypes.py:224 +#: dcim/tables/devices.py:625 dcim/tables/devicetypes.py:224 #: templates/dcim/interface.html:66 msgid "Management Only" msgstr "Только управление" -#: dcim/tables/devices.py:629 +#: dcim/tables/devices.py:633 msgid "Wireless link" msgstr "Беспроводная связь" -#: dcim/tables/devices.py:639 +#: dcim/tables/devices.py:643 msgid "VDCs" msgstr "Виртуальные контексты устройств(VDCs)" -#: dcim/tables/devices.py:647 dcim/tables/devicetypes.py:48 +#: dcim/tables/devices.py:651 dcim/tables/devicetypes.py:48 #: dcim/tables/devicetypes.py:140 dcim/views.py:1080 dcim/views.py:2023 #: netbox/navigation/menu.py:91 templates/dcim/device/base.html:52 #: templates/dcim/device_list.html:71 templates/dcim/devicetype/base.html:49 @@ -5629,7 +5625,7 @@ msgstr "Виртуальные контексты устройств(VDCs)" msgid "Inventory Items" msgstr "Предметы инвентаря" -#: dcim/tables/devices.py:728 +#: dcim/tables/devices.py:732 #: templates/circuits/inc/circuit_termination.html:80 #: templates/dcim/consoleport.html:81 templates/dcim/consoleserverport.html:81 #: templates/dcim/frontport.html:53 templates/dcim/frontport.html:125 @@ -5638,28 +5634,28 @@ msgstr "Предметы инвентаря" msgid "Rear Port" msgstr "Задний порт" -#: dcim/tables/devices.py:893 templates/dcim/modulebay.html:51 +#: dcim/tables/devices.py:897 templates/dcim/modulebay.html:51 msgid "Installed Module" msgstr "Установленный модуль" -#: dcim/tables/devices.py:896 +#: dcim/tables/devices.py:900 msgid "Module Serial" msgstr "Серийный номер модуля" -#: dcim/tables/devices.py:900 +#: dcim/tables/devices.py:904 msgid "Module Asset Tag" msgstr "Тег активов модуля" -#: dcim/tables/devices.py:909 +#: dcim/tables/devices.py:913 msgid "Module Status" msgstr "Состояние модуля" -#: dcim/tables/devices.py:951 dcim/tables/devicetypes.py:308 +#: dcim/tables/devices.py:955 dcim/tables/devicetypes.py:308 #: templates/dcim/inventoryitem.html:41 msgid "Component" msgstr "Компонент" -#: dcim/tables/devices.py:1006 +#: dcim/tables/devices.py:1010 msgid "Items" msgstr "Предметы" @@ -5777,7 +5773,7 @@ msgstr "Высота" #: dcim/tables/racks.py:85 msgid "Space" -msgstr "Космос" +msgstr "Пространство" #: dcim/tables/racks.py:96 templates/dcim/rack.html:105 msgid "Outer Width" @@ -5797,7 +5793,7 @@ msgstr "Максимальный вес" #: ipam/tables/asn.py:66 netbox/navigation/menu.py:16 #: netbox/navigation/menu.py:18 msgid "Sites" -msgstr "ЦОД" +msgstr "Сайты" #: dcim/tests/test_api.py:49 msgid "Test case must set peer_termination_type" @@ -5882,7 +5878,7 @@ msgstr "JSON" #: extras/choices.py:36 msgid "Selection" -msgstr "Отбор" +msgstr "Выбор" #: extras/choices.py:37 msgid "Multiple selection" @@ -6226,7 +6222,7 @@ msgid "Cluster type (slug)" msgstr "Тип кластера (подстрока)" #: extras/filtersets.py:490 ipam/forms/bulk_edit.py:475 -#: ipam/forms/model_forms.py:585 virtualization/forms/filtersets.py:108 +#: ipam/forms/model_forms.py:589 virtualization/forms/filtersets.py:108 msgid "Cluster group" msgstr "Кластерная группа" @@ -6278,7 +6274,7 @@ msgstr "Видимый пользовательский интерфейс" #: extras/forms/bulk_edit.py:58 extras/forms/bulk_import.py:63 #: extras/forms/filtersets.py:83 extras/models/customfields.py:200 msgid "UI editable" -msgstr "Редактируемый пользовательский интерфейс" +msgstr "Редактируемый UI" #: extras/forms/bulk_edit.py:63 extras/forms/filtersets.py:86 msgid "Is cloneable" @@ -6357,8 +6353,8 @@ msgid "Is active" msgstr "Активен" #: extras/forms/bulk_import.py:34 extras/forms/bulk_import.py:115 -#: extras/forms/bulk_import.py:130 extras/forms/bulk_import.py:153 -#: extras/forms/bulk_import.py:177 extras/forms/filtersets.py:114 +#: extras/forms/bulk_import.py:136 extras/forms/bulk_import.py:159 +#: extras/forms/bulk_import.py:183 extras/forms/filtersets.py:114 #: extras/forms/filtersets.py:160 extras/forms/filtersets.py:201 #: extras/forms/model_forms.py:43 extras/forms/model_forms.py:127 #: extras/forms/model_forms.py:156 extras/forms/model_forms.py:197 @@ -6367,8 +6363,8 @@ msgid "Content types" msgstr "Типы контента" #: extras/forms/bulk_import.py:36 extras/forms/bulk_import.py:117 -#: extras/forms/bulk_import.py:132 extras/forms/bulk_import.py:155 -#: extras/forms/bulk_import.py:179 tenancy/forms/bulk_import.py:96 +#: extras/forms/bulk_import.py:138 extras/forms/bulk_import.py:161 +#: extras/forms/bulk_import.py:185 tenancy/forms/bulk_import.py:96 msgid "One or more assigned object types" msgstr "Один или несколько назначенных типов объектов" @@ -6417,29 +6413,40 @@ msgstr "" "дополнительными метками, разделенными двоеточием: «Choice1:First Choice, " "Choice2:Second Choice»" -#: extras/forms/bulk_import.py:182 +#: extras/forms/bulk_import.py:120 extras/models/models.py:353 +msgid "button class" +msgstr "класс кнопок" + +#: extras/forms/bulk_import.py:123 extras/models/models.py:357 +msgid "" +"The class of the first link in a group will be used for the dropdown button" +msgstr "" +"Класс первой ссылки в группе будет использоваться для кнопки раскрывающегося" +" списка" + +#: extras/forms/bulk_import.py:188 msgid "Action object" msgstr "Объект действия" -#: extras/forms/bulk_import.py:184 +#: extras/forms/bulk_import.py:190 msgid "Webhook name or script as dotted path module.Class" msgstr "Имя веб-хука или скрипт в виде пунктирного пути module.Class" -#: extras/forms/bulk_import.py:205 +#: extras/forms/bulk_import.py:211 #, python-brace-format msgid "Webhook {name} not found" msgstr "Вебхук {name} не найден" -#: extras/forms/bulk_import.py:214 +#: extras/forms/bulk_import.py:220 #, python-brace-format msgid "Script {name} not found" msgstr "Сценарий {name} не найден" -#: extras/forms/bulk_import.py:236 +#: extras/forms/bulk_import.py:242 msgid "Assigned object type" msgstr "Назначенный тип объекта" -#: extras/forms/bulk_import.py:241 +#: extras/forms/bulk_import.py:247 msgid "The classification of entry" msgstr "Классификация записей" @@ -6522,7 +6529,7 @@ msgstr "Группы сайтов" #: extras/forms/filtersets.py:364 extras/forms/model_forms.py:408 #: netbox/navigation/menu.py:21 msgid "Locations" -msgstr "Местоположения" +msgstr "Локации" #: extras/forms/filtersets.py:369 extras/forms/model_forms.py:413 msgid "Device types" @@ -6897,7 +6904,8 @@ msgstr "Допустимы только буквенно-цифровые сим #: extras/models/customfields.py:101 msgid "Double underscores are not permitted in custom field names." msgstr "" -"В именах настраиваемых полей недопустимо использовать двойное подчеркивание." +"В именах настраиваемых полей недопустимо использовать два подчеркивания " +"подряд (зарезервировано)." #: extras/models/customfields.py:112 msgid "" @@ -7321,10 +7329,9 @@ msgid "" "username, request_id, and data." msgstr "" "Шаблон Jinja2 для настраиваемого тела запроса. Если поле пусто, будет " -"добавлен объект JSON, представляющий изменение. Доступные контекстные данные" -" включают: событие, модель, отметка " -"времени, имя пользователя, идентификатор " -"запроса, и данные." +"добавлен объект JSON с изменениями. Доступные контекстные данные включают: " +"event, model, timestamp, " +"username, request_id, и data." #: extras/models/models.py:234 msgid "secret" @@ -7393,17 +7400,6 @@ msgstr "Код Jinja2 шаблона для URL-адреса" msgid "Links with the same group will appear as a dropdown menu" msgstr "Ссылки с той же группой появятся в выпадающем меню" -#: extras/models/models.py:353 -msgid "button class" -msgstr "класс кнопок" - -#: extras/models/models.py:357 -msgid "" -"The class of the first link in a group will be used for the dropdown button" -msgstr "" -"Класс первой ссылки в группе будет использоваться для кнопки раскрывающегося" -" списка" - #: extras/models/models.py:360 msgid "new window" msgstr "новое окно" @@ -7496,11 +7492,11 @@ msgstr "ширина изображения" #: extras/models/models.py:640 msgid "image attachment" -msgstr "вложение изображения" +msgstr "прикрепить изображение" #: extras/models/models.py:641 msgid "image attachments" -msgstr "вложения изображений" +msgstr "прикрепленные изображения" #: extras/models/models.py:655 #, python-brace-format @@ -7590,7 +7586,7 @@ msgid "staged changes" msgstr "поэтапные изменения" #: extras/models/tags.py:40 -msgid "The object type(s) to which this this tag can be applied." +msgid "The object type(s) to which this tag can be applied." msgstr "Тип (ы) объекта, к которому можно применить этот тег." #: extras/models/tags.py:49 @@ -7889,7 +7885,7 @@ msgid "VLAN number (1-4094)" msgstr "Номер VLAN (1-4094)" #: ipam/filtersets.py:437 ipam/filtersets.py:441 ipam/filtersets.py:533 -#: ipam/forms/model_forms.py:444 templates/tenancy/contact.html:54 +#: ipam/forms/model_forms.py:430 templates/tenancy/contact.html:54 #: tenancy/forms/bulk_edit.py:112 msgid "Address" msgstr "Адрес" @@ -7931,7 +7927,7 @@ msgstr "Интерфейс виртуальной машины (ID)" #: ipam/filtersets.py:614 msgid "FHRP group (ID)" -msgstr "Группа FHRP (идентификатор)" +msgstr "FHRP группа (ID)" #: ipam/filtersets.py:618 msgid "Is assigned to an interface" @@ -8006,7 +8002,7 @@ msgstr "Является частным" #: templates/ipam/aggregate.html:19 templates/ipam/asn.html:28 #: templates/ipam/asnrange.html:20 templates/ipam/rir.html:20 msgid "RIR" -msgstr "ВСАДНИКИ" +msgstr "RIR" #: ipam/forms/bulk_edit.py:168 msgid "Date added" @@ -8019,7 +8015,7 @@ msgstr "Длина префикса" #: ipam/forms/bulk_edit.py:252 ipam/forms/filtersets.py:236 #: templates/ipam/prefix.html:86 msgid "Is a pool" -msgstr "Это бассейн" +msgstr "Это пул" #: ipam/forms/bulk_edit.py:257 ipam/forms/bulk_edit.py:301 #: ipam/forms/filtersets.py:243 ipam/forms/filtersets.py:282 @@ -8058,7 +8054,7 @@ msgid "Authentication key" msgstr "Ключ аутентификации" #: ipam/forms/bulk_edit.py:404 ipam/forms/filtersets.py:369 -#: ipam/forms/model_forms.py:455 netbox/navigation/menu.py:376 +#: ipam/forms/model_forms.py:441 netbox/navigation/menu.py:376 #: templates/ipam/fhrpgroup.html:51 #: templates/wireless/inc/authentication_attrs.html:5 #: wireless/forms/bulk_edit.py:90 wireless/forms/bulk_edit.py:137 @@ -8073,13 +8069,13 @@ msgstr "Минимальное количество VLAN VID для детей" #: ipam/forms/bulk_edit.py:420 msgid "Maximum child VLAN VID" -msgstr "Максимальное количество идентификаторов VLAN для детей" +msgstr "Максимальный ID дочерней VLAN" -#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:527 +#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:531 msgid "Scope type" msgstr "Тип прицела" -#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:600 +#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:604 #: ipam/tables/vlans.py:71 templates/ipam/vlangroup.html:39 msgid "Scope" msgstr "Область применения" @@ -8088,8 +8084,8 @@ msgstr "Область применения" msgid "Site & Group" msgstr "Сайт и группа" -#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:663 -#: ipam/forms/model_forms.py:697 ipam/tables/services.py:19 +#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:667 +#: ipam/forms/model_forms.py:701 ipam/tables/services.py:19 #: ipam/tables/services.py:49 templates/ipam/service.html:39 #: templates/ipam/servicetemplate.html:24 msgid "Ports" @@ -8129,7 +8125,7 @@ msgid "Parent device of assigned interface (if any)" msgstr "Родительское устройство назначенного интерфейса (если есть)" #: ipam/forms/bulk_import.py:310 ipam/forms/bulk_import.py:496 -#: ipam/forms/model_forms.py:691 virtualization/filtersets.py:284 +#: ipam/forms/model_forms.py:695 virtualization/filtersets.py:284 #: virtualization/filtersets.py:323 virtualization/forms/bulk_edit.py:199 #: virtualization/forms/bulk_edit.py:325 #: virtualization/forms/bulk_import.py:146 @@ -8179,16 +8175,12 @@ msgstr "Тип прицела (приложение и модель)" #: ipam/forms/bulk_import.py:418 #, python-brace-format msgid "Minimum child VLAN VID (default: {minimum})" -msgstr "" -"Минимальное количество идентификаторов VLAN для детей (по умолчанию): " -"{minimum})" +msgstr "Минимальный ID дочерней VLAN (по умолчанию: {minimum})" #: ipam/forms/bulk_import.py:424 #, python-brace-format msgid "Maximum child VLAN VID (default: {maximum})" -msgstr "" -"Максимальное количество идентификаторов VLAN для детей (по умолчанию): " -"{maximum})" +msgstr "Максимальный ID дочерней VLAN (по умолчанию: {maximum})" #: ipam/forms/bulk_import.py:448 msgid "Assigned VLAN group" @@ -8249,7 +8241,7 @@ msgstr "Ассортимент" #: ipam/forms/filtersets.py:127 msgid "Start" -msgstr "Начните" +msgstr "Начало" #: ipam/forms/filtersets.py:131 msgid "End" @@ -8286,7 +8278,7 @@ msgstr "DNS-имя" #: ipam/forms/filtersets.py:401 ipam/forms/filtersets.py:494 #: ipam/models/vlans.py:156 templates/ipam/vlan.html:34 msgid "VLAN ID" -msgstr "ИДЕНТИФИКАТОР КЛАНА" +msgstr "VLAN ID" #: ipam/forms/filtersets.py:433 msgid "Minimum VID" @@ -8311,8 +8303,8 @@ msgstr "Порт" #: virtualization/forms/filtersets.py:189 #: virtualization/forms/filtersets.py:234 #: virtualization/forms/model_forms.py:223 -#: virtualization/tables/virtualmachines.py:115 -#: virtualization/tables/virtualmachines.py:168 vpn/choices.py:45 +#: virtualization/tables/virtualmachines.py:128 +#: virtualization/tables/virtualmachines.py:181 vpn/choices.py:45 #: vpn/forms/filtersets.py:289 vpn/forms/model_forms.py:161 #: vpn/forms/model_forms.py:172 vpn/forms/model_forms.py:274 msgid "Virtual Machine" @@ -8335,7 +8327,7 @@ msgstr "Назначение сайта/VLAN" msgid "IP Range" msgstr "Диапазон IP-адресов" -#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:454 +#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:440 #: templates/ipam/fhrpgroup.html:19 templates/ipam/ipaddress_edit.html:52 msgid "FHRP Group" msgstr "Группа компаний FHRP" @@ -8348,7 +8340,7 @@ msgstr "Сделайте этот IP-адрес основным для устр msgid "An IP address can only be assigned to a single object." msgstr "IP-адрес можно присвоить только одному объекту." -#: ipam/forms/model_forms.py:357 ipam/models/ip.py:877 +#: ipam/forms/model_forms.py:357 ipam/models/ip.py:896 msgid "" "Cannot reassign IP address while it is designated as the primary IP for the " "parent object" @@ -8363,34 +8355,25 @@ msgstr "" "В качестве основных IP-адресов можно назначить только IP-адреса, назначенные" " интерфейсу." -#: ipam/forms/model_forms.py:373 -#, python-brace-format -msgid "{ip} is a network ID, which may not be assigned to an interface." -msgstr "" -"{ip} это сетевой идентификатор, который не может быть присвоен интерфейсу." - -#: ipam/forms/model_forms.py:379 -#, python-brace-format -msgid "" -"{ip} is a broadcast address, which may not be assigned to an interface." -msgstr "" -"{ip} это широковещательный адрес, который может не быть присвоен интерфейсу." - -#: ipam/forms/model_forms.py:456 +#: ipam/forms/model_forms.py:442 msgid "Virtual IP Address" msgstr "Виртуальный IP-адрес" -#: ipam/forms/model_forms.py:598 ipam/forms/model_forms.py:637 +#: ipam/forms/model_forms.py:523 +msgid "Assignment already exists" +msgstr "Задание уже существует" + +#: ipam/forms/model_forms.py:602 ipam/forms/model_forms.py:641 #: ipam/tables/ip.py:250 templates/ipam/vlan_edit.html:37 #: templates/ipam/vlangroup.html:27 msgid "VLAN Group" msgstr "Группа VLAN" -#: ipam/forms/model_forms.py:599 +#: ipam/forms/model_forms.py:603 msgid "Child VLANs" msgstr "Детские сети VLAN" -#: ipam/forms/model_forms.py:668 ipam/forms/model_forms.py:702 +#: ipam/forms/model_forms.py:672 ipam/forms/model_forms.py:706 msgid "" "Comma-separated list of one or more port numbers. A range may be specified " "using a hyphen." @@ -8398,15 +8381,15 @@ msgstr "" "Список одного или нескольких номеров портов, разделенных запятыми. Диапазон " "можно указать с помощью дефиса." -#: ipam/forms/model_forms.py:673 templates/ipam/servicetemplate.html:12 +#: ipam/forms/model_forms.py:677 templates/ipam/servicetemplate.html:12 msgid "Service Template" msgstr "Шаблон Службы" -#: ipam/forms/model_forms.py:724 +#: ipam/forms/model_forms.py:728 msgid "Service template" msgstr "Шаблон службы" -#: ipam/forms/model_forms.py:754 +#: ipam/forms/model_forms.py:758 msgid "" "Must specify name, protocol, and port(s) if not using a service template." msgstr "" @@ -8415,7 +8398,7 @@ msgstr "" #: ipam/models/asns.py:34 msgid "start" -msgstr "начните" +msgstr "Начало" #: ipam/models/asns.py:51 msgid "ASN range" @@ -8485,7 +8468,7 @@ msgstr "IP-пространство, управляемое этим RIR, счи #: ipam/models/ip.py:71 netbox/navigation/menu.py:170 msgid "RIRs" -msgstr "РИР" +msgstr "RIR's" #: ipam/models/ip.py:83 msgid "IPv4 or IPv6 network" @@ -8555,7 +8538,7 @@ msgstr "Основная функция этого префикса" #: ipam/models/ip.py:264 msgid "is a pool" -msgstr "это бассейн" +msgstr "это пул" #: ipam/models/ip.py:266 msgid "All IP addresses within this prefix are considered usable" @@ -8573,12 +8556,12 @@ msgstr "префиксы" msgid "Cannot create prefix with /0 mask." msgstr "Невозможно создать префикс с маской /0." -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 #, python-brace-format msgid "VRF {vrf}" msgstr "VRF {vrf}" -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 msgid "global table" msgstr "глобальная таблица" @@ -8674,12 +8657,25 @@ msgstr "IP-адреса" msgid "Cannot create IP address with /0 mask." msgstr "Невозможно создать IP-адрес с маской /0." -#: ipam/models/ip.py:856 +#: ipam/models/ip.py:850 +#, python-brace-format +msgid "{ip} is a network ID, which may not be assigned to an interface." +msgstr "" +"{ip} это идентификатор сети, который не может быть присвоен интерфейсу." + +#: ipam/models/ip.py:861 +#, python-brace-format +msgid "" +"{ip} is a broadcast address, which may not be assigned to an interface." +msgstr "" +"{ip} это широковещательный адрес, который может не быть присвоен интерфейсу." + +#: ipam/models/ip.py:875 #, python-brace-format msgid "Duplicate IP address found in {table}: {ipaddress}" msgstr "Дубликат IP-адреса обнаружен в {table}: {ipaddress}" -#: ipam/models/ip.py:883 +#: ipam/models/ip.py:902 msgid "Only IPv6 addresses can be assigned SLAAC status" msgstr "Только адресам IPv6 можно присвоить статус SLAAC" @@ -8719,19 +8715,19 @@ msgstr "Служба должна быть связана с устройств #: ipam/models/vlans.py:49 msgid "minimum VLAN ID" -msgstr "минимальный идентификатор VLAN" +msgstr "минимальный VLAN ID" #: ipam/models/vlans.py:55 msgid "Lowest permissible ID of a child VLAN" -msgstr "Наименьший допустимый идентификатор дочерней VLAN" +msgstr "Наименьший допустимый ID дочерней VLAN" #: ipam/models/vlans.py:58 msgid "maximum VLAN ID" -msgstr "максимальный идентификатор VLAN" +msgstr "максимальный VLAN ID" #: ipam/models/vlans.py:64 msgid "Highest permissible ID of a child VLAN" -msgstr "Максимально допустимый идентификатор детской VLAN" +msgstr "Максимально допустимый ID дочерней VLAN" #: ipam/models/vlans.py:85 msgid "VLAN groups" @@ -8761,7 +8757,7 @@ msgstr "Группа VLAN (опционально)" #: ipam/models/vlans.py:161 msgid "Numeric VLAN ID (1-4094)" -msgstr "Цифровой идентификатор VLAN (1-4094)" +msgstr "Цифровой VLAN ID (1-4094)" #: ipam/models/vlans.py:179 msgid "Operational status of this VLAN" @@ -8874,7 +8870,7 @@ msgstr "Глубина" #: ipam/tables/ip.py:261 msgid "Pool" -msgstr "Бассейн" +msgstr "Пул" #: ipam/tables/ip.py:264 ipam/tables/ip.py:317 msgid "Marked Utilized" @@ -8908,7 +8904,7 @@ msgstr "Тип прицела" #: ipam/tables/vlans.py:107 ipam/tables/vlans.py:210 #: templates/dcim/inc/interface_vlans_table.html:4 msgid "VID" -msgstr "ВИДЕО" +msgstr "VID" #: ipam/tables/vrfs.py:30 msgid "RD" @@ -9086,21 +9082,20 @@ msgstr "Предпочитайте адреса IPv4, а не IPv6" #: netbox/config/parameters.py:84 msgid "Rack unit height" -msgstr "Высота стеллажа" +msgstr "Высота стойки" #: netbox/config/parameters.py:86 msgid "Default unit height for rendered rack elevations" msgstr "" -"Высота единиц измерения по умолчанию для визуализированных высот стеллажей" +"Высота единиц измерения по умолчанию для визуализированных высот стоек" #: netbox/config/parameters.py:91 msgid "Rack unit width" -msgstr "Ширина стеллажа" +msgstr "Ширина стойки" #: netbox/config/parameters.py:93 msgid "Default unit width for rendered rack elevations" -msgstr "" -"Ширина единиц измерения по умолчанию для визуализированных высот стеллажей" +msgstr "Ширина юнита по умолчанию для визуализированных высот стоек" #: netbox/config/parameters.py:100 msgid "Powerfeed voltage" @@ -9254,7 +9249,7 @@ msgstr "{class_name} необходимо указать класс модели #: netbox/models/features.py:278 #, python-brace-format msgid "Unknown field name '{name}' in custom field data." -msgstr "Неизвестное имя поля '{name}'в данных произвольных полей." +msgstr "Неизвестное имя поля '{name}' в данных для настраиваемых полей." #: netbox/models/features.py:284 #, python-brace-format @@ -9339,7 +9334,7 @@ msgstr "Роли устройств" #: netbox/navigation/menu.py:68 templates/dcim/device.html:162 #: templates/dcim/virtualdevicecontext.html:8 msgid "Virtual Device Contexts" -msgstr "Контексты виртуальных устройств" +msgstr "Виртуальные контексты" #: netbox/navigation/menu.py:76 msgid "Manufacturers" @@ -9351,7 +9346,7 @@ msgstr "Компоненты устройства" #: netbox/navigation/menu.py:92 templates/dcim/inventoryitemrole.html:8 msgid "Inventory Item Roles" -msgstr "Роли предметов инвентаря" +msgstr "Роли предметов" #: netbox/navigation/menu.py:99 netbox/navigation/menu.py:103 msgid "Connections" @@ -9371,15 +9366,15 @@ msgstr "Интерфейсные подключения" #: netbox/navigation/menu.py:114 msgid "Console Connections" -msgstr "Подключения к консоли" +msgstr "Консольные подключения" #: netbox/navigation/menu.py:119 msgid "Power Connections" -msgstr "Подключения питания" +msgstr "Подключения кабелей питания" #: netbox/navigation/menu.py:135 msgid "Wireless LAN Groups" -msgstr "Группы беспроводных локальных сетей" +msgstr "Группы WLAN" #: netbox/navigation/menu.py:156 msgid "Prefix & VLAN Roles" @@ -9410,7 +9405,7 @@ msgstr "VPN" #: netbox/navigation/menu.py:203 netbox/navigation/menu.py:205 #: vpn/tables/tunnels.py:24 msgid "Tunnels" -msgstr "Тоннели" +msgstr "Туннели" #: netbox/navigation/menu.py:206 templates/vpn/tunnelgroup.html:8 msgid "Tunnel Groups" @@ -9423,12 +9418,12 @@ msgstr "Окончание туннелей" #: netbox/navigation/menu.py:211 netbox/navigation/menu.py:213 #: vpn/models/l2vpn.py:64 msgid "L2VPNs" -msgstr "VPN-сервисы L2P" +msgstr "L2VPN" #: netbox/navigation/menu.py:214 templates/vpn/l2vpn.html:57 #: templates/vpn/tunnel.html:73 vpn/tables/tunnels.py:58 msgid "Terminations" -msgstr "Прекращения" +msgstr "Соединения" #: netbox/navigation/menu.py:220 msgid "IKE Proposals" @@ -9464,7 +9459,7 @@ msgstr "Виртуальные машины" #: templates/virtualization/virtualmachine.html:177 #: templates/virtualization/virtualmachine/base.html:32 #: templates/virtualization/virtualmachine_list.html:21 -#: virtualization/tables/virtualmachines.py:90 virtualization/views.py:389 +#: virtualization/tables/virtualmachines.py:103 virtualization/views.py:389 msgid "Virtual Disks" msgstr "Виртуальные диски" @@ -9474,19 +9469,19 @@ msgstr "Типы кластеров" #: netbox/navigation/menu.py:247 msgid "Cluster Groups" -msgstr "Кластерные группы" +msgstr "Группы кластеров" #: netbox/navigation/menu.py:261 msgid "Circuit Types" -msgstr "Типы цепей" +msgstr "Типы каналов связи" #: netbox/navigation/menu.py:265 netbox/navigation/menu.py:267 msgid "Providers" -msgstr "Поставщики" +msgstr "Провайдеры" #: netbox/navigation/menu.py:268 templates/circuits/provider.html:53 msgid "Provider Accounts" -msgstr "Учетные записи поставщиков" +msgstr "Аккаунты провайдеров" #: netbox/navigation/menu.py:269 msgid "Provider Networks" @@ -9546,7 +9541,7 @@ msgstr "Сохраненные фильтры" #: netbox/navigation/menu.py:316 msgid "Image Attachments" -msgstr "Вложения изображений" +msgstr "Прикрепленные Изображения" #: netbox/navigation/menu.py:320 msgid "Reports & Scripts" @@ -9576,7 +9571,7 @@ msgstr "Вебхуки" #: netbox/views/generic/feature_views.py:151 #: templates/extras/report/base.html:37 templates/extras/script/base.html:36 msgid "Jobs" -msgstr "Вакансии" +msgstr "Задачи" #: netbox/navigation/menu.py:362 msgid "Logging" @@ -9819,7 +9814,7 @@ msgstr "Журнал изменений" #: netbox/views/generic/feature_views.py:91 msgid "Journal" -msgstr "журнал" +msgstr "Журнал" #: netbox/views/generic/object_views.py:105 #, python-brace-format @@ -10090,7 +10085,7 @@ msgstr "Документы" #: templates/base/layout.html:139 templates/rest_framework/api.html:10 msgid "REST API" -msgstr "ОСТАЛЬНОЕ API" +msgstr "REST API" #: templates/base/layout.html:144 msgid "REST API documentation" @@ -10150,7 +10145,7 @@ msgstr "Сведения об увольнении" #: templates/circuits/circuittype.html:10 msgid "Add Circuit" -msgstr "Добавить цепь" +msgstr "Добавить канал связи" #: templates/circuits/inc/circuit_termination.html:9 #: templates/dcim/devicetype/component_templates.html:33 @@ -10181,7 +10176,7 @@ msgstr "Обмен" #: templates/circuits/inc/circuit_termination.html:26 #, python-format msgid "Termination %(side)s" -msgstr "Прекращение %(side)s" +msgstr "Окончания %(side)s" #: templates/circuits/inc/circuit_termination.html:42 #: templates/dcim/cable.html:70 templates/dcim/cable.html:76 @@ -10262,7 +10257,7 @@ msgstr "Патч-панель/порт" #: templates/circuits/provider.html:11 msgid "Add circuit" -msgstr "Добавить цепь" +msgstr "Добавить канал связи" #: templates/circuits/provideraccount.html:17 msgid "Provider Account" @@ -10270,11 +10265,11 @@ msgstr "Учетная запись поставщика" #: templates/core/configrevision.html:47 msgid "Default unit height" -msgstr "Высота единицы измерения по умолчанию" +msgstr "Высота юнита по умолчанию" #: templates/core/configrevision.html:51 msgid "Default unit width" -msgstr "Ширина блока по умолчанию" +msgstr "Ширина юнита по умолчанию" #: templates/core/configrevision.html:63 msgid "Default voltage" @@ -10390,8 +10385,8 @@ msgstr "Планирование" #: templates/core/job.html:66 #, python-format -msgid "every %(interval)s seconds" -msgstr "каждый %(interval)s секунды" +msgid "every %(interval)s minutes" +msgstr "каждый %(interval)s протокол" #: templates/dcim/bulk_disconnect.html:9 #, python-format @@ -10645,7 +10640,7 @@ msgstr "Контекстные данные" #: templates/dcim/device/render_config.html:57 #: templates/virtualization/virtualmachine/render_config.html:57 msgid "Download" -msgstr "Загрузить" +msgstr "Скачать" #: templates/dcim/device/render_config.html:60 #: templates/virtualization/virtualmachine/render_config.html:60 @@ -10689,7 +10684,7 @@ msgstr "Отсек для устройств" #: templates/dcim/devicebay.html:48 msgid "Installed Device" -msgstr "Установленное устройство" +msgstr "Вставленное устройство" #: templates/dcim/devicebay_delete.html:6 #, python-format @@ -10745,7 +10740,7 @@ msgstr "Номер детали" #: templates/dcim/devicetype.html:40 msgid "Height (U" -msgstr "Высота (U)" +msgstr "Высота (U" #: templates/dcim/devicetype.html:44 msgid "Exclude From Utilization" @@ -10757,7 +10752,7 @@ msgstr "Родитель/ребенок" #: templates/dcim/devicetype.html:74 msgid "Front Image" -msgstr "Изображение на передней панели" +msgstr "Изображение спереди" #: templates/dcim/devicetype.html:86 msgid "Rear Image" @@ -10779,7 +10774,7 @@ msgstr "Состояние подключения" #: templates/dcim/inc/cable_termination.html:65 msgid "No termination" -msgstr "Без увольнения" +msgstr "Без окончания" #: templates/dcim/inc/cable_toggle_buttons.html:4 msgid "Mark Planned" @@ -10804,7 +10799,7 @@ msgstr "Конечные точки пути" #: templates/dcim/inc/endpoint_connection.html:8 #: templates/dcim/powerfeed.html:128 templates/dcim/rearport.html:101 msgid "Not connected" -msgstr "Не подключен" +msgstr "Не подключено" #: templates/dcim/inc/interface_vlans_table.html:6 msgid "Untagged" @@ -10855,7 +10850,7 @@ msgstr "Беспроводная связь" #: templates/dcim/interface.html:226 vpn/choices.py:55 msgid "Peer" -msgstr "сверстник" +msgstr "Peer" #: templates/dcim/interface.html:238 #: templates/wireless/inc/wirelesslink_interface.html:26 @@ -10883,7 +10878,7 @@ msgstr "Ширина канала" #: wireless/forms/filtersets.py:79 wireless/models.py:81 #: wireless/models.py:155 wireless/tables/wirelesslan.py:44 msgid "SSID" -msgstr "СКАЗАЛ" +msgstr "SSID" #: templates/dcim/interface.html:316 msgid "LAG Members" @@ -10906,7 +10901,7 @@ msgstr "Родительский товар" #: templates/dcim/inventoryitem.html:49 msgid "Part ID" -msgstr "Идентификатор детали" +msgstr "Номер модели" #: templates/dcim/inventoryitem_bulk_delete.html:5 msgid "This will also delete all child inventory items of those listed" @@ -10924,15 +10919,15 @@ msgstr "Розетка питания" #: templates/dcim/location.html:17 msgid "Add Child Location" -msgstr "Добавить местоположение ребенка" +msgstr "Добавить дочернюю локацию" #: templates/dcim/location.html:76 msgid "Child Locations" -msgstr "Местонахождение детей" +msgstr "Дочерние локации" #: templates/dcim/location.html:84 templates/dcim/site.html:137 msgid "Add a Location" -msgstr "Добавить местоположение" +msgstr "Добавить локацию" #: templates/dcim/location.html:98 templates/dcim/site.html:151 msgid "Add a Device" @@ -10970,7 +10965,7 @@ msgstr "A" #: templates/dcim/poweroutlet.html:51 msgid "Feed Leg" -msgstr "Кормовая ножка" +msgstr "Фаза электропитания" #: templates/dcim/powerpanel.html:77 msgid "Add Power Feeds" @@ -10978,11 +10973,11 @@ msgstr "Добавить каналы питания" #: templates/dcim/powerport.html:47 msgid "Maximum Draw" -msgstr "Максимальная ничья" +msgstr "Максимальное потребление" #: templates/dcim/powerport.html:51 msgid "Allocated Draw" -msgstr "Распределенная ничья" +msgstr "Выделенная мощность" #: templates/dcim/rack.html:66 msgid "Space Utilization" @@ -10990,7 +10985,7 @@ msgstr "Использование пространства" #: templates/dcim/rack.html:96 msgid "descending" -msgstr "спускаясь" +msgstr "по убыванию" #: templates/dcim/rack.html:96 msgid "ascending" @@ -10998,7 +10993,7 @@ msgstr "по возрастанию" #: templates/dcim/rack.html:99 msgid "Starting Unit" -msgstr "Пусковой блок" +msgstr "Начальный юнит" #: templates/dcim/rack.html:125 msgid "Mounting Depth" @@ -11018,7 +11013,7 @@ msgstr "Общий вес" #: templates/dcim/rack.html:173 templates/dcim/rack_elevation_list.html:16 msgid "Images and Labels" -msgstr "Изображения и этикетки" +msgstr "Изображения и лейблы" #: templates/dcim/rack.html:174 templates/dcim/rack_elevation_list.html:17 msgid "Images only" @@ -11026,7 +11021,7 @@ msgstr "Только изображения" #: templates/dcim/rack.html:175 templates/dcim/rack_elevation_list.html:18 msgid "Labels only" -msgstr "Только этикетки" +msgstr "Только лейблы" #: templates/dcim/rack/reservations.html:9 msgid "Add reservation" @@ -11042,7 +11037,7 @@ msgstr "Внешние размеры" #: templates/dcim/rack_edit.html:56 templates/dcim/rack_edit.html:71 msgid "Unit" -msgstr "Единица" +msgstr "Юнит" #: templates/dcim/rack_elevation_list.html:12 msgid "View List" @@ -11078,7 +11073,7 @@ msgstr "Добавить сайт" #: templates/dcim/region.html:56 msgid "Child Regions" -msgstr "Детские регионы" +msgstr "Дочерние регионы" #: templates/dcim/region.html:64 msgid "Add Region" @@ -11106,7 +11101,7 @@ msgstr "Физический адрес" #: templates/dcim/site.html:81 msgid "Map" -msgstr "карта" +msgstr "Карта" #: templates/dcim/site.html:92 msgid "Shipping Address" @@ -11116,7 +11111,7 @@ msgstr "Адрес доставки" #: templates/tenancy/tenantgroup.html:58 #: templates/wireless/wirelesslangroup.html:56 msgid "Child Groups" -msgstr "Детские группы" +msgstr "Дочерние группы" #: templates/dcim/sitegroup.html:64 msgid "Add Site Group" @@ -11155,7 +11150,7 @@ msgstr "Редактирование виртуального корпуса %(n #: templates/dcim/virtualchassis_edit.html:54 msgid "Rack/Unit" -msgstr "Стойка/блок" +msgstr "Стойка/Юнит" #: templates/dcim/virtualchassis_remove_member.html:5 msgid "Remove Virtual Chassis Member" @@ -11292,7 +11287,7 @@ msgstr "Автор" #: templates/extras/admin/plugins_list.html:25 msgid "Author Email" -msgstr "Электронная почта автора" +msgstr "Почта" #: templates/extras/admin/plugins_list.html:27 #: templates/vpn/ipsecprofile.html:47 vpn/forms/bulk_edit.py:140 @@ -11357,7 +11352,7 @@ msgstr "Видимый пользовательский интерфейс" #: templates/extras/customfield.html:86 msgid "UI Editable" -msgstr "Редактируемый пользовательский интерфейс" +msgstr "Редактируемый UI" #: templates/extras/customfield.html:108 msgid "Validation Rules" @@ -11422,15 +11417,15 @@ msgstr "Пока не добавлено ни одной закладки." #: templates/extras/dashboard/widgets/objectcounts.html:15 msgid "No permission" -msgstr "Нет разрешения" +msgstr "Нет прав" #: templates/extras/dashboard/widgets/objectlist.html:6 msgid "No permission to view this content" -msgstr "Нет разрешения на просмотр этого контента" +msgstr "Нет прав на просмотр этого контента" #: templates/extras/dashboard/widgets/objectlist.html:10 msgid "Unable to load content. Invalid view name" -msgstr "Невозможно загрузить содержимое. Неверное имя представления" +msgstr "Невозможно загрузить содержимое. Неверное имя" #: templates/extras/dashboard/widgets/rssfeed.html:12 msgid "No content found" @@ -11486,7 +11481,7 @@ msgstr "Уровень" #: templates/extras/htmx/report_result.html:46 #: templates/extras/htmx/script_result.html:27 msgid "Message" -msgstr "Послание" +msgstr "Сообщение" #: templates/extras/htmx/script_result.html:21 msgid "Script Log" @@ -11571,7 +11566,7 @@ msgstr "Разница" #: templates/extras/objectchange.html:87 msgid "Previous" -msgstr "Предыдущее" +msgstr "Предыдущий" #: templates/extras/objectchange.html:90 msgid "Next" @@ -11614,7 +11609,7 @@ msgstr "Этот отчет недействителен и не может бы #: templates/extras/report.html:23 templates/extras/report_list.html:88 msgid "Run Again" -msgstr "Беги снова" +msgstr "Повторить" #: templates/extras/report.html:25 templates/extras/report_list.html:90 msgid "Run Report" @@ -11622,7 +11617,7 @@ msgstr "Запустить отчет" #: templates/extras/report.html:36 msgid "Last run" -msgstr "Последний забег" +msgstr "Последний запуск" #: templates/extras/report/base.html:30 msgid "Report" @@ -11630,7 +11625,7 @@ msgstr "Отчет" #: templates/extras/report_list.html:48 templates/extras/script_list.html:54 msgid "Last Run" -msgstr "Последний забег" +msgstr "Последний запуск" #: templates/extras/report_list.html:70 templates/extras/script_list.html:77 msgid "Never" @@ -11659,7 +11654,7 @@ msgstr "" #: templates/extras/script.html:13 msgid "You do not have permission to run scripts" -msgstr "У вас нет разрешения на запуск сценариев" +msgstr "У вас нет разрешения на запуск скриптов" #: templates/extras/script.html:37 msgid "Run Script" @@ -11676,7 +11671,7 @@ msgstr "" #: templates/extras/script_list.html:91 msgid "No Scripts Found" -msgstr "Сценарии не найдены" +msgstr "Скрипты не найдены" #: templates/extras/script_list.html:94 #, python-format @@ -11689,11 +11684,11 @@ msgstr "" #: templates/extras/script_result.html:42 msgid "Log" -msgstr "журнал" +msgstr "Журнал" #: templates/extras/tag.html:35 msgid "Tagged Items" -msgstr "Помеченные товары" +msgstr "Элементы с тэгом" #: templates/extras/tag.html:47 msgid "Allowed Object Types" @@ -11729,7 +11724,7 @@ msgstr "Дополнительные заголовки" #: templates/extras/webhook.html:85 msgid "Body Template" -msgstr "Шаблон тела" +msgstr "Шаблон тела запроса" #: templates/generic/bulk_add_component.html:15 msgid "Bulk Creation" @@ -11826,8 +11821,8 @@ msgid "" "%(example)s would identify a VRF by its route distinguisher." msgstr "" "На связанные объекты можно ссылаться с помощью любого уникального атрибута. " -"Например, %(example)s будет идентифицировать VRF по " -"идентификатору маршрута." +"Например, %(example)s будет идентифицировать VRF по индикатору " +"маршрута." #: templates/generic/bulk_remove.html:13 msgid "Confirm Bulk Removal" @@ -12015,11 +12010,11 @@ msgstr "Теги не назначены" #: templates/inc/profile_button.html:12 templates/inc/profile_button.html:62 msgid "Dark Mode" -msgstr "Темный режим" +msgstr "Темная тема" #: templates/inc/profile_button.html:45 msgid "Log Out" -msgstr "Выйти из системы" +msgstr "Выйти" #: templates/inc/profile_button.html:53 msgid "Log In" @@ -12131,7 +12126,7 @@ msgstr "Назначение интерфейса" #: templates/ipam/ipaddress_edit.html:74 msgid "NAT IP (Inside" -msgstr "NAT IP (внутренний)" +msgstr "NAT IP (внутренний" #: templates/ipam/iprange.html:20 msgid "Starting Address" @@ -12175,7 +12170,7 @@ msgstr "Сетевая маска" #: templates/ipam/prefix.html:195 msgid "Wildcard Mask" -msgstr "Маска подстановочных знаков" +msgstr "Обратная маска" #: templates/ipam/prefix.html:199 msgid "Broadcast Address" @@ -12211,7 +12206,7 @@ msgstr "Импорт VRF" #: templates/ipam/routetarget.html:49 msgid "Exporting VRFs" -msgstr "Экспорт файлов VRF" +msgstr "Экспорт VRF" #: templates/ipam/routetarget.html:60 msgid "Importing L2VPNs" @@ -12248,11 +12243,11 @@ msgstr "Добавить VLAN" #: templates/ipam/vlangroup.html:43 msgid "Permitted VIDs" -msgstr "Разрешенные видео" +msgstr "Разрешенные VID" #: templates/ipam/vrf.html:19 msgid "Route Distinguisher" -msgstr "Дифференцировщик маршрута" +msgstr "RD" #: templates/ipam/vrf.html:32 msgid "Unique IP Space" @@ -12277,11 +12272,11 @@ msgstr "Переключить цветовой режим" #: templates/media_failure.html:7 msgid "Static Media Failure - NetBox" -msgstr "Сбой статического носителя - NetBox" +msgstr "Ошибка статичных медиа - NetBox" #: templates/media_failure.html:21 msgid "Static Media Failure" -msgstr "Сбой статического носителя" +msgstr "Ошибка статичных медиа" #: templates/media_failure.html:23 msgid "The following static media file failed to load" @@ -12297,9 +12292,9 @@ msgid "" " This installs the most recent iteration of each static file into the static" " root path." msgstr "" -"manage.py собирает статические данные был запущен во время " -"последнего обновления. При этом последняя итерация каждого статического " -"файла устанавливается в статический корневой путь." +"manage.py collectstatic была запущен во время последнего " +"обновления. При этом последняя итерация каждого статического файла " +"устанавливается в статический корневой путь." #: templates/media_failure.html:35 #, python-format @@ -12308,10 +12303,8 @@ msgid "" "the STATIC_ROOT path. Refer to the " "installation documentation for further guidance." msgstr "" -"Служба HTTP (например, nginx или Apache) настроена на обслуживание файлов из" -" СТАТИЧЕСКИЙ КОРЕНЬ путь. Обратитесь к документация по установке для получения " -"дополнительных рекомендаций." +"Служба HTTP (например, nginx или Apache) настроена на обслуживание файлов из STATIC_ROOT\n" +" путь. Обратитесь к документация по установке для получения дополнительных рекомендаций." #: templates/media_failure.html:47 #, python-format @@ -12438,7 +12431,7 @@ msgstr "Дисковое пространство" #: templates/virtualization/virtualmachine.html:147 msgctxt "Abbreviation for gigabyte" msgid "GB" -msgstr "ГИГАБАЙТ" +msgstr "ГБ" #: templates/virtualization/cluster/base.html:18 msgid "Add Virtual Machine" @@ -12506,7 +12499,7 @@ msgstr "Версия IKE" #: templates/vpn/ikepolicy.html:30 msgid "Pre-Shared Key" -msgstr "Предварительный общий ключ" +msgstr "Pre-Shared ключ" #: templates/vpn/ikepolicy.html:34 #: templates/wireless/inc/authentication_attrs.html:21 @@ -12585,15 +12578,15 @@ msgstr "Атрибуты L2VPN" #: templates/vpn/l2vpn.html:65 templates/vpn/tunnel.html:81 msgid "Add a Termination" -msgstr "Добавить увольнение" +msgstr "Добавить окончание" #: templates/vpn/l2vpntermination_edit.html:9 msgid "L2VPN Termination" -msgstr "Прекращение действия L2VPN" +msgstr "L2VPN окончания" #: templates/vpn/tunnel.html:9 msgid "Add Termination" -msgstr "Добавить прекращение" +msgstr "Добавить окончание" #: templates/vpn/tunnel.html:38 vpn/forms/bulk_edit.py:48 #: vpn/forms/bulk_import.py:48 vpn/forms/filtersets.py:56 @@ -12622,7 +12615,7 @@ msgstr "Туннельная группа" #: templates/vpn/tunneltermination.html:10 msgid "Tunnel Termination" -msgstr "Прекращение туннеля" +msgstr "Окончание Туннеля" #: templates/vpn/tunneltermination.html:36 vpn/forms/bulk_import.py:107 #: vpn/forms/model_forms.py:101 vpn/forms/model_forms.py:137 @@ -12632,7 +12625,7 @@ msgstr "Внешний IP-адрес" #: templates/vpn/tunneltermination.html:53 msgid "Peer Terminations" -msgstr "Прекращение контрактов со стороны коллег" +msgstr "Конечные Точки" #: templates/wireless/inc/authentication_attrs.html:13 msgid "Cipher" @@ -12640,7 +12633,7 @@ msgstr "Шифр" #: templates/wireless/inc/authentication_attrs.html:17 msgid "PSK" -msgstr "ПСК" +msgstr "PSK" #: templates/wireless/inc/wirelesslink_interface.html:35 #: templates/wireless/inc/wirelesslink_interface.html:45 @@ -12675,7 +12668,7 @@ msgstr "Свойства ссылки" #: tenancy/choices.py:19 msgid "Tertiary" -msgstr "Высшее образование" +msgstr "Третичный" #: tenancy/choices.py:20 msgid "Inactive" @@ -12743,7 +12736,7 @@ msgstr "контактные роли" #: tenancy/models/contacts.py:68 msgid "title" -msgstr "титул" +msgstr "название" #: tenancy/models/contacts.py:73 msgid "phone" @@ -12751,7 +12744,7 @@ msgstr "телефон" #: tenancy/models/contacts.py:78 msgid "email" -msgstr "письмо" +msgstr "email" #: tenancy/models/contacts.py:87 msgid "link" @@ -12896,7 +12889,7 @@ msgid "" msgstr "" "Разрешенные сети IPv4/IPv6, из которых можно использовать токен. Оставьте " "поле пустым, чтобы не было ограничений. Пример: 10.1.1.0/24, " -"192.168.10.16/32, 2001 год: дБ 8:1:/64" +"192.168.10.16/32, 2001:DB8:1::/64" #: users/forms/model_forms.py:177 msgid "Confirm password" @@ -12947,102 +12940,102 @@ msgstr "Ограничения для этого типа объектов не msgid "Invalid filter for {model}: {error}" msgstr "Неверный фильтр для {model}: {error}" -#: users/models.py:54 +#: users/models.py:55 msgid "user" msgstr "пользователя" -#: users/models.py:55 +#: users/models.py:56 msgid "users" msgstr "пользователей" -#: users/models.py:66 +#: users/models.py:67 msgid "A user with this username already exists." msgstr "Пользователь с таким именем уже существует." -#: users/models.py:78 vpn/models/crypto.py:42 +#: users/models.py:79 vpn/models/crypto.py:42 msgid "group" msgstr "группа" -#: users/models.py:79 +#: users/models.py:80 msgid "groups" msgstr "групп" -#: users/models.py:106 users/models.py:107 +#: users/models.py:107 users/models.py:108 msgid "user preferences" msgstr "пользовательские настройки" -#: users/models.py:174 +#: users/models.py:175 #, python-brace-format msgid "Key '{path}' is a leaf node; cannot assign new keys" msgstr "Ключ '{path}'является листовым узлом; не может назначать новые ключи" -#: users/models.py:186 +#: users/models.py:187 #, python-brace-format msgid "Key '{path}' is a dictionary; cannot assign a non-dictionary value" msgstr "" "Ключ '{path}'— словарь; не может присвоить значение, отличное от словаря" -#: users/models.py:252 +#: users/models.py:253 msgid "expires" msgstr "истекает" -#: users/models.py:257 +#: users/models.py:258 msgid "last used" msgstr "последний раз использованный" -#: users/models.py:262 +#: users/models.py:263 msgid "key" msgstr "ключ" -#: users/models.py:268 +#: users/models.py:269 msgid "write enabled" msgstr "запись включена" -#: users/models.py:270 +#: users/models.py:271 msgid "Permit create/update/delete operations using this key" msgstr "" "Разрешить операции создания/обновления/удаления с использованием этого ключа" -#: users/models.py:281 +#: users/models.py:282 msgid "allowed IPs" msgstr "разрешенные IP-адреса" -#: users/models.py:283 +#: users/models.py:284 msgid "" "Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for" " no restrictions. Ex: \"10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64\"" msgstr "" "Разрешенные сети IPv4/IPv6, из которых можно использовать токен. Оставьте " "поле пустым, чтобы не было ограничений. Пример: «10.1.1.0/24, " -"192.168.10.16/32, 2001: БД 8:1: /64»" +"192.168.10.16/32, 2001:DB8:1::/64»" -#: users/models.py:291 +#: users/models.py:296 msgid "token" msgstr "токен" -#: users/models.py:292 +#: users/models.py:297 msgid "tokens" msgstr "токены" -#: users/models.py:373 +#: users/models.py:378 msgid "The list of actions granted by this permission" msgstr "Список действий, предусмотренных этим разрешением" -#: users/models.py:378 +#: users/models.py:383 msgid "constraints" msgstr "ограничения" -#: users/models.py:379 +#: users/models.py:384 msgid "" "Queryset filter matching the applicable objects of the selected type(s)" msgstr "" "Фильтр Queryset, соответствующий применимым объектам выбранного типа (типов)" -#: users/models.py:386 +#: users/models.py:391 msgid "permission" msgstr "разрешение" -#: users/models.py:387 +#: users/models.py:392 msgid "permissions" msgstr "разрешения" @@ -13065,7 +13058,7 @@ msgstr "Роза" #: utilities/choices.py:139 msgid "Fuchsia" -msgstr "фуксия" +msgstr "Фуксия" #: utilities/choices.py:141 msgid "Dark Purple" @@ -13077,7 +13070,7 @@ msgstr "Светло-синий" #: utilities/choices.py:147 msgid "Aqua" -msgstr "вода" +msgstr "Бирюзовый" #: utilities/choices.py:148 msgid "Dark Green" @@ -13093,7 +13086,7 @@ msgstr "Лайм" #: utilities/choices.py:153 msgid "Amber" -msgstr "янтарь" +msgstr "Янтарь" #: utilities/choices.py:155 msgid "Dark Orange" @@ -13101,7 +13094,7 @@ msgstr "Темно-оранжевый" #: utilities/choices.py:156 msgid "Brown" -msgstr "коричневый" +msgstr "Коричневый" #: utilities/choices.py:157 msgid "Light Grey" @@ -13312,45 +13305,54 @@ msgstr "" "Этот объект был изменен с момента визуализации формы. Подробности см. в " "журнале изменений объекта." -#: utilities/forms/utils.py:42 utilities/forms/utils.py:65 -#: utilities/forms/utils.py:77 utilities/forms/utils.py:80 +#: utilities/forms/utils.py:42 utilities/forms/utils.py:68 +#: utilities/forms/utils.py:85 utilities/forms/utils.py:87 #, python-brace-format msgid "Range \"{value}\" is invalid." msgstr "Ассортимент»{value}\"недействительно." -#: utilities/forms/utils.py:225 +#: utilities/forms/utils.py:74 +#, python-brace-format +msgid "" +"Invalid range: Ending value ({end}) must be greater than beginning value " +"({begin})." +msgstr "" +"Неверный диапазон: конечное значение ({end}) должно быть больше начального " +"значения ({begin})." + +#: utilities/forms/utils.py:232 #, python-brace-format msgid "Duplicate or conflicting column header for \"{field}\"" msgstr "Повторяющийся или конфликтующий заголовок столбца для»{field}»" -#: utilities/forms/utils.py:231 +#: utilities/forms/utils.py:238 #, python-brace-format msgid "Duplicate or conflicting column header for \"{header}\"" msgstr "Повторяющийся или конфликтующий заголовок столбца для»{header}»" -#: utilities/forms/utils.py:240 +#: utilities/forms/utils.py:247 #, python-brace-format msgid "Row {row}: Expected {count_expected} columns but found {count_found}" msgstr "" "Ряд {row}: Ожидается {count_expected} столбцы, но найдены {count_found}" -#: utilities/forms/utils.py:263 +#: utilities/forms/utils.py:270 #, python-brace-format msgid "Unexpected column header \"{field}\" found." msgstr "Неожиданный заголовок столбца»{field}«найдено." -#: utilities/forms/utils.py:265 +#: utilities/forms/utils.py:272 #, python-brace-format msgid "Column \"{field}\" is not a related object; cannot use dots" msgstr "" "Столбец»{field}\"не является родственным объектом; нельзя использовать точки" -#: utilities/forms/utils.py:269 +#: utilities/forms/utils.py:276 #, python-brace-format msgid "Invalid related object attribute for column \"{field}\": {to_field}" msgstr "Неверный атрибут связанного объекта для столбца»{field}«: {to_field}" -#: utilities/forms/utils.py:277 +#: utilities/forms/utils.py:284 #, python-brace-format msgid "Required column header \"{header}\" not found." msgstr "Обязательный заголовок столбца»{header}\"не найден." @@ -13407,7 +13409,7 @@ msgstr "Закладка" #: utilities/templates/buttons/clone.html:4 msgid "Clone" -msgstr "Клон" +msgstr "Клонировать" #: utilities/templates/buttons/export.html:4 msgid "Export" @@ -13467,7 +13469,7 @@ msgstr "Ничего не назначено" #: utilities/templates/widgets/markdown_input.html:6 msgid "Write" -msgstr "Напишите" +msgstr "Текст" #: utilities/templates/widgets/markdown_input.html:20 msgid "Testing" @@ -13768,19 +13770,19 @@ msgstr "IP-адрес в IP-адресе" #: vpn/choices.py:34 msgid "GRE" -msgstr "СЕРЫЙ" +msgstr "GRE" #: vpn/choices.py:56 msgid "Hub" -msgstr "хаб" +msgstr "Hub" #: vpn/choices.py:57 msgid "Spoke" -msgstr "Говорил" +msgstr "Spoke" #: vpn/choices.py:80 msgid "Aggressive" -msgstr "агрессивный" +msgstr "Агрессивный" #: vpn/choices.py:81 msgid "Main" @@ -13788,7 +13790,7 @@ msgstr "Главная" #: vpn/choices.py:92 msgid "Pre-shared keys" -msgstr "Предварительно общие ключи" +msgstr "PSK" #: vpn/choices.py:93 msgid "Certificates" @@ -13891,7 +13893,7 @@ msgstr "Группа туннелей" #: vpn/forms/bulk_edit.py:116 vpn/models/crypto.py:47 msgid "SA lifetime" -msgstr "На всю жизнь" +msgstr "Время жизни SA" #: vpn/forms/bulk_edit.py:150 wireless/forms/bulk_edit.py:78 #: wireless/forms/bulk_edit.py:125 wireless/forms/filtersets.py:63 @@ -13966,7 +13968,7 @@ msgstr "Назначенный интерфейс (устройство или #: vpn/forms/bulk_import.py:334 msgid "Cannot import device and VM interface terminations simultaneously." msgstr "" -"Невозможно одновременно импортировать терминалы интерфейса устройства и " +"Невозможно одновременно сетевые окончания интерфейса устройства и " "виртуальной машины." #: vpn/forms/bulk_import.py:336 @@ -13990,17 +13992,22 @@ msgstr "Предложение" msgid "Assigned Object Type" msgstr "Назначенный тип объекта" +#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 +#: vpn/forms/model_forms.py:241 vpn/tables/tunnels.py:91 +msgid "Tunnel interface" +msgstr "Туннельный интерфейс" + #: vpn/forms/model_forms.py:147 msgid "First Termination" -msgstr "Первое увольнение" +msgstr "Первая точка" #: vpn/forms/model_forms.py:151 msgid "Second Termination" -msgstr "Второе расторжение" +msgstr "Вторая точка" #: vpn/forms/model_forms.py:198 msgid "This parameter is required when defining a termination." -msgstr "Этот параметр необходим при определении прекращения." +msgstr "Этот параметр необходим при определении точки." #: vpn/forms/model_forms.py:320 vpn/forms/model_forms.py:355 msgid "Policy" @@ -14102,11 +14109,11 @@ msgstr "Профили IPsec" #: vpn/models/l2vpn.py:116 msgid "L2VPN termination" -msgstr "Завершение работы L2VPN" +msgstr "L2VPN соединение" #: vpn/models/l2vpn.py:117 msgid "L2VPN terminations" -msgstr "Прекращения работы L2VPN" +msgstr "L2VPN соединения" #: vpn/models/l2vpn.py:135 #, python-brace-format @@ -14140,7 +14147,7 @@ msgstr "идентификатор туннеля" #: vpn/models/tunnels.py:94 msgid "tunnel" -msgstr "тоннель" +msgstr "туннель" #: vpn/models/tunnels.py:95 msgid "tunnels" @@ -14156,7 +14163,7 @@ msgstr "завершение туннеля" #: vpn/models/tunnels.py:157 msgid "tunnel terminations" -msgstr "терминалы туннелей" +msgstr "точки подключения туннеля" #: vpn/models/tunnels.py:174 #, python-brace-format @@ -14197,11 +14204,11 @@ msgstr "Родитель объекта" #: vpn/tables/l2vpn.py:74 msgid "Object Site" -msgstr "Объектный сайт" +msgstr "Сайт объекта" #: vpn/tables/tunnels.py:88 msgid "Host" -msgstr "Хозяин" +msgstr "Хост" #: wireless/choices.py:11 msgid "Access point" @@ -14209,7 +14216,7 @@ msgstr "Точка доступа" #: wireless/choices.py:12 msgid "Station" -msgstr "станция" +msgstr "Станция" #: wireless/choices.py:467 msgid "Open" @@ -14264,19 +14271,19 @@ msgstr "беспроводная локальная сеть" #: wireless/models.py:143 msgid "interface A" -msgstr "интерфейс A" +msgstr "Интерфейс A" #: wireless/models.py:150 msgid "interface B" -msgstr "интерфейс B" +msgstr "Интерфейс B" #: wireless/models.py:198 msgid "wireless link" -msgstr "беспроводная связь" +msgstr "беспроводное соединение" #: wireless/models.py:199 msgid "wireless links" -msgstr "беспроводные ссылки" +msgstr "беспроводные соединения" #: wireless/models.py:216 wireless/models.py:222 #, python-brace-format diff --git a/netbox/translations/tr/LC_MESSAGES/django.mo b/netbox/translations/tr/LC_MESSAGES/django.mo index f6d54ea9ab2403a1bc54939c0505908f1d5666ac..7e2a0e4b93733e55824f421bcea428dd12904e6f 100644 GIT binary patch delta 61603 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

  • W7F5v-m0$-q~;MZ8s{8chMQ24801vEAF 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 s{aSP{ zoWuH9cn#;@wd}qoMA#dR{CYHiY3TW#k0Wpsdakcp8v-bgu60#3Lv7IsbVu(WiUzt3 zo%x4of1hC&%)BlcQr~l3Naa{Gpf}Mk9B-q${|B^#6X*bcpeapzE(BT_UDNXD^%m&! z-O)WV1r1~=I>ATLfS*ZHu;Z7|O|l71=?Cac_v1kP3F~0X=fkhtZ$j66H`-yw3nB2z zXn)nC4Wcchoua*?$-xwiY*chS8rh9l6KBWv7tk4PzzX;kI^#=dpjW;a1}=jJR5jKc zpqslTdhGh6uijC}9!Mr0qhJSLpr20PqXAq%1H0^{km{W1o+yc)_nOhx(Z1*aV=%RM z(9Jy`{T=b+=yPvIKTgT{J4nF?e~TCXLm$lha(J*J`T}W-p6`L^jAllcVCt7j(f6Z2 zVg~o6y%Gj4g=Ve}I>8|re*UN6W}1iwG7lYa2|AN!&^3Jxufey`7f`|VVb3%|Q`!OT zIEilByV2+F!&dkxy2rjl-?%?x>fisn7#nhJ2s0~zK3ET3g68NsZX4}_4%8dHZwQ*& z(Xl=QoycvmeKC4WpFk(JDb{yyh@byQX)vM-XeRQ!8g49uE=5E1dT+F&(b2hRh8{v^ z{49EWUqoN&>#+lFM+40DT6*Gnyc*MS*K3@AZ~TM?2RMY~@eg$7MK{Ldg`Vrm=s=Co zRJMxtKnEU*W@H3<3dW=T+!URIj&nPj!3UER-2E%j2%kgG`xdmLD_;*8D2WTGH%5=$ zHoO-1V-0MyDeQ^yXa<*{13Zf^-OK2E<#lxL?27Hl-zd0y&tW+%_(qs%6U7xGszBuVOpuyRkK1@n*Q*AKeR6qqkxO>h~ghA(_}n!Bp)+Q~MRV<_FQp z|BhZlGnHj?$W(r;L%j_8P9K2I_+B*Nhj9$xWov@i|qa8Mkc1AlMg6^4{(Jv~uqJgbI1AYO`*lXzhCo$=o zW!@G(r8=YU_=#w$m!NOD$6|d0dRn%kss0Sz1N&qBFZ5JgiuK&@hM5*dm$EV%U@de{ zG=4XJ{&$EMx}e9bFZ#epbQ6w8XFM4V>~{3N#n=E>U@iO#yJGh3q23SsQ@;m&?ngBB zzoM`5Oz$N_N=v;L2CRV2tU5YCLv$@W#P&h4eN=3phNk#V^mG1UbcS!B=l)&vy|4rA z@8jr!=*c7nXZlZUNW33rnit&*CDHR-4}HgX!In4=P4)Ze675CrJBiLP@j*DwS70jD zvEBizb9oXvZgM3BJ9;+yDms&Q(M@v%edV4)QK#rjk`xD6&|Ni@q@IZbv^;aTa3=*ZVE)GO@{}QZ#Z=t8-D4OcuuowP=E>)M0 z!W;2g^e3GqXdu6!0bW99o^PjLa5(>kDR`kHy4ePzYjth(2K0ej&;aM7GgyqS{Zr@+ zH^lmfXnObP0N3MVx|eqQ}qxo4@Y{hN6GaJ>F!yq@Xf@O(aW$%|sLDTN9YjCeFUvm0am4m2b8p#waG2JjRb=nH6O zwnX1UJKlx%w+{{QNUWbhpSy@|);zm8|K3n$cNn-fI#36+gTCmR4nsHNRCIIAM(?`~ zy>D@J8Jd}=(ZDvM=Y9*iL?59O`VL+4!~W5VH=Lot7sVwUfc-xSKaQ_QKOM_|8Xjzm z4%`XdeErY>hQ<1LbcWN=`{tt=yDwf}j!xuhG@$2`@rHNM2X~@NuphhPujq|UKMN`A zi4Ccb!N&L``f>anI-~#4_d%Y|L*Ql609vD&>x$mj8*jnnND4-D7@g5cH05W|4~Z;$ z!u1R^unK5LP0@i{qo4QJp-Z_M4PYahnccDd8+1vIq0gTVCKG>B@C9=o9WejiaAOs8 zEo-Bjsx8`aZ}k3g@%ru2rO{{6&9w=g`6tnDFf~zhX-;Ab&;L0JM%v(uuoqgRH+DyF z?1yG#BpS%gvAzKPczy&8^p$vhd%XU6ynZBJ{|{?(U*0dn61K+F|Nnan1ygbtn!?3s z04rks88nbr(Iwax>tCUP|A_99(`Y~GUxkTe!#dQjKzDsd^m;e6zdo4!g2HeLWw74Y z;lp7NK1O{ZcEk$bgrAP5U?1x5p=)1cU-(63L!3{22yVi|=qr2q{_r#8hv;Vg2Yuhv zJrG`SQx9h4Zicw<5H~kZ3yH;Y(o7U`bMntUDzYnU^VKKu|2NDT6hdIu;{_? z=4_0WsE@+FxCB?=Pk2AhKg9VTPNDGkVQpq&BkC{2`f>C_sMrtT&v<%aRq9FfE7l8W zAUm-W{)3&d&Ear;3HsIU1vFzHq2I7hU{B1I{4v}(6dhtnyted zsb@VJ{!ZsiG_{-1_rjm(l2!XDJuwkypf9$ckiDL0ek^=r+KN@EC;z5UokH2;;R{Az zyq@|ZG{Unu9Xp%|-*(?dQ~nb=K(&+Mw_T5+r{i6mf}i0`Z2fZ>XbT$HN$iJpPNn{h zh-6|Bg=cBFfa7r0FX6c5J{{INBiaZ}Wgm1+C*!keY%+9HwfZe=;#+Yp^=GgZmiawQ zYz(?|k7G;x7`vL2Jb#2As|VxVT-bnqCNw=0j!Q?h-aR@9eN&FXD{wLv#0BV{SRUIq zp~r1EX2!kfvHJ#nUmV3euF1LBkmb*C?yp48a|Sv<9dy%l#Bw+Ro!JBE%)df+{}q3Q z8Ma63)6rM%n&?aDL^fh-sW53opHc8b;XCw0=rkI@Mf44q|7=*2%IN*g&>3__U&TW( zJ5EP4lSF5H7v{iw(dQn)cK8hXqC0gqe*Rye!QGqVTo|YgW>D`J>oa0~A^J_|dGyQZ z%d!3`cBQ@-M_|dn!;5ErbR)Vaj-vsO`6nFr^^)QVzh9)aN8A)S>V=w!pnu7O(g>ocl(2 z9rZh6{UkbrdjEyZ)(t%ks!yVO=^u0tbxR$j#`UcpdHNGj#8Ki)QW@^fV+cg^%k(XlART_qD{_9NS)@&cFZ1iWzG#1AV*5?# zDNA~t@e}`}V2YlMH@p^acpIJhC(%9e`d8@M9zth$0uB6kH1+?YGtQqm1Y87tU(}2B znV3QSAxygYUZ+qBPoXKyds(K`k6L4~BK7;w(?(Se4bdtog4 z+)d~b+=0GumPMb9*EgW!Y>)L%v-1Bx8POpcj5IA<=%5g~2`i#|pcPia-qE>Ojrv-2 zpl>ksPDhvW9M;F2*+XVqqXCXa_sUJ^1QsVLxQ5HH0zQd$xD%bp*Vq70qI;oqj?i8n zO=%i6)?HzYquD{b+xu&?Wl^OJL)iVN)gtQE*Mip{bmP268((qv!B{xCJ+3 z*IdjbjbE|k4x6e-o=mAPD1*`O1#8f?{SEE!EZTqOydls$=;XF>Nw?q~@#V>QwH>Y)KQL7#7jW~e7R;4pNeQ_%@5EWr6U z@})Ew=}YJvY&W__KcXG~iazic+R;U{qsy-d164!^s)OFw8hx${+D|WZf|{NJz6>D=zBTeqtFnVC!`lZ=Fyg1YQJQdV! zVFxJTOQ7y8fuMHu6x2z526ZxjEgmYqhaVnP!FZtV-YLzWKR)+=6)uNEg&TmncHKb< zjs+z+-PZFBSAyEfW>B|{L!gfSIw<}lP`8m+p!*pSDE+7jJR3<1>g0+i;QsG^WWu3> zT|wOj`rCRqs2xrORbW0S;nkon)lN{?_L!|tfzr7Ss=+`|`5!?w=9AEaVL<6caWPSc z2|y)e1680ns2f#vP)A)Clt3d;m!Ku6Bkcj|1P6i2p8_gxDX5LC26da*V)31zZd<27 z)p6ZpBEh$yI{E|Z5iM*Y4>1|26Ut@lvY;+eL-ThBB{ULL{!CCCS_(>cC#VxT0jjYx zpc=a5iMyPeOjPhWs7F5E#GV}`0wtIQRDlAZc3i>YwLxFjZ9rX$_MmPHeLyuh0aOFa zL0uAmP$#s{{O3XU|Np+nMAzu0;df94LnZMD!~#_yB`AM3TNeY>U}aF3pgyRbcL2p3 z3aa2#Fa@{_)JdEIbxF^G?*IRL*#fuaAaEa4=g&Z0!>^zUIY~W&;lU&<6M%{r0@Yw? zP`nzT9{HMpYNQ{i2FHTZoe%2Xunu(TZg9j14?qch1eNF{^W0q`gZlX(EvN^Y3ZU}3 zfx2W9L4EsP2kIK10(ImUK%ML@!{?xTL!fw0a_;{khD`1WL)(|N@ zJC6@)$7w)4HJ}>a2kJymSo|ueyeFU&Cs!)DVjaCL#sEzr1f@)w4s7p2t)CSg=e-ntF z%h|<5M|2QWBbROc5R|}YP{MyfH5fj%=SCGBR9;e0jb=B0AyE7ZpbFLnrP~;$NjxNh-R z<`0(6v(u=cek4l>&I0R$g~2cBxc}#5k}JLE$K?)SKGw^@QeYsM2~3+InEOTPx?n@r zhr!%nvW&spzf!3WW@kOo)<-}+V0|}?pULw~st}k0e?u@2I5ZRY|E^5-;wT7a%^b}A z_M6UNY1XH~^k9rEo=Z{`EW)}RSREV(<^==6`e1^ro}U@}fGJt8GCU7T=PT#~%4+Lc z=;yyeO!jcWyXldGoA@x+7hp{R!?Ui=tuqwYCy%ZXmY;8XBN?(1#OlGx$afTdQ@Mu5 zQSg=)*j(eKWnKy24m6rj>koNJ(RcMW(ajWm1F1O|{wzVh=Q#XO^FOBB#&86Ebo0fg z!fQeCvgFoBd?Ugu5KWFoCGajo){>mi%$MTt1aBRfkAbm zO?NN;P6Y8vE$jU4|6v zoP#OQ2QvRB-fiC~vXP{y#D>Gm#|Vz^4T(+38HE2A*cSFwcn^r>$F~Sxd721`-fH4M z@GbR7y1(*~08UN(Tiri@;~GYS*RGQ|7D8q^Zw&SaGg9!kb$^Ej`I057x(uK%_|c-qQAdyOW`#nZ9za*)v%}~ z-X^wz0@>h&A{G*S1kV?-+;FZT)Q5F-%UcXLG3#oqkHU#ZT=tk+;oz?)f4|$q{sR!N z2uT(O0>43X#)20)UfF!a#)G@@^@CdmT){d4Ih!o+C^_Zu$Av2k&;7js>+q~oQr!Jd z2Ji2~hoenNd^otP2K$PR)I}ox zjG~m_he!T7zgyDbcXn?3ihmm_?ZDWyGr{9_X22c5$V#rPH}OuKOIdNjj;v$C{|GK& z$euBFP_w=svid{%z>pQeIUO;t{U#wE4aCI1g#sOHI~5_+wwwWM;5f0q6iLNAC;5ko z9Wm|_a_7N6NPIG}*l;Gne~)$vean)iph#m&IBPp@WZhVSV3D6jb2mT0REm=3Gz%x5F50iHi!+D8cmV|jj_*Kz~ z;?Z_FGnsUPaEpRxnTG>6SfN-Xq(^Wl4NRkm*EX;&#JXw_UtIE*;olCQ&*E^dklPu( z%V@VG79D?5_~}d}9I;>_x&DiBCTGV%Z955iPWzQuOcD!Ne3oGZA;Qn$HDk1q}VAO-NhIJC=*>!K`Ck^KVj+}ISAK{iJ7Q%}CASOGAa1N4X zKM*d1|0g1{9^{T-sCGLv$AWw$+W)iB5Wd>!o@pW+X0r&EvobbMrr(*xmrw$srh zr(lf1C+lnE5*AO4;6X%qUyjoe!K-jevp!|K_Et=}3#_3;Y_7Zc^P;gpcvp&?XUwB;VQ@V8yYP>-P22+;;Ga!%r`#VIO7J73`6kNJNk)-0 z;2MZki1{;yGV)Volr<@y>=GyVnph9UH)74yEDgn=z+rs*(cQp^&N@CLI1OZG+@!(x zdi<}&_=)ghNUfNkM^LYA$ciAaoY)OSbAYXgWddcV;RiKdC~(r@iwerJlCz8rOkuu? zVntY|4{|w9;dd26a5+U~2kGhy$=9_5tSVv^h$Vp|E?pJxp(0*`qb@m&9iba?~3}m=zaqHtQmm!>bogVq$#8r`sN~$(G*hpPE2z+x1W|G9v?Hf)&3- za%0w;L0MiC)%=bXjEwM2+oAljkZi<q3eH<0{a^S5Fpy*cn^lk^N?MoXT-7_>Qy+g z$&;;P%q8}LoLlCTe=s}0NmGU3mL}eqwfp>+u-MAttXO6xAYNcBplBhQx%;nUo*@UB zH=@Wa1bb0vsfyZ3Oa(g!+3^hY`5ms?!t-?*IWJhd8nBpz$h{z){$`y9LPT&a#ab|5 z2Dv9FJ7z`7PymXvfptZSuV-Bi&T-bG$(zmC=+5A-48E}ZQ}FgO;_6pMb5TfY0deop zNJE09==cJ$uB@+6{H}GM7w!m(6(jb60>3$-=-@Scx$wmS$H8qcA7e84^{ffrx9jj8 zU}qb?($sLDe@_^NIe_E4x6 zMA+LFx=VRAXtgDEIDHc^S@~4WuBCr#vYfzT{E;cr25u8O4RIrx_AU6=^^(;gg4J+F=SDLbXKK4KO+_R;MPxq^_1Y}r zI}zo4erdBkv`=GHyZkUmM3I7GV*N0$~&LS6~&A z)*&pbY^M_y?l?w6Tkl|=osr)PG-ET<$dzTl_nu}(6R&`FZ#J=l`4jJE*z6457yTfH zAt?sru$)3J@FxPp@DHMap(a=yEN+GL3!78coQlbEQe*<+YY^@W&!0G-Qvc89GcSt2 zIGnI{O0LFsZMIpF&4@&VxDHZH$SW+K1F_5qmx9*|8tZTCC(&Pri=c`(}|K(wE2+`@*@kB^o z8_4`5BOS40BveE25Bwt(?`)(RhO6MM;sp8dcxM^78{Rs62W(TLSj*lK+Xbf>caycgoKs3b>cJfYF9a3;gwNbXOHWhVZC^)@ST6wWc$ zGX0)30~*udt^%inDQNDVUjLD`g&e_7;)NykMko}->sCC_cH6}Y%pk84ITaZ*$XN+q zp{dUtxvV;Qhrml{+#uF~bzQKS=`SGmK)*_m#ixM)5}UDZW5=F{MA=R|jqB!@FA|Rv zE3F}|Wx-6?v*N~wW-DuE5xM)BPbViCn2=^t!FR<+NcNV(i3k*eaF}@j#gBoR5#g_o z+;*R@C5Ruvx1LzHAeU*darB$xaR~D}U?&W`!*JfYv(YhZg?}#b&!O%5M`DqKafmMO zQoyc?o8E^`*O3s?cGnKUD)7?cj|^_I9Zn>^mfUMJvJp-e;)$8h0JFi%$QW)K`_PlM zQl)kz$r_k*5lP?hT?HTGkBh(q=FQo$jNi9AQLL!P!)?ce#~2oa@3q_H_d@R%*cFX- zrW2QjmTRZ=Ospq?n23jfoY%hNdmt%w#Q1GN} zD!p+#Tdg=?W#X>qBo#vJ2nA#j=sXI(u&fW@Z*DtDWVe%ha86Pn3mAvpK4m_f`9%ty zqDU4yftc1zTH*o3*PFirnB5xO<-PtLNGt*A5nUyLaK)OrWZm-zKh6pKui51_n!3b@ z%6cS4UXt^Q@g31KmWPzv`j8iioI3cgSkYkQ>|y;D4ficA7J(@&cG^zsk&uqU_bDLT z=YfA;TM(D6MJO6V3E-V1-{0eQzO$LX_Nwk;QCdPuh@z(|S;g5&T1HJxRl(8GJ z<2Xwpwg{ZasO#>Kr_uP%BHDrE3;1Q@!6xL%YB9e_^WWihV!ecQa*8x1rxyOLj0udT z`0rCEKRjK{HK1;PZW{w30>Kz`^$p?itQR4Yg7rWeIf~dA1ez1u$$S%IDgHz><+Yov z!=o{Z+)H*!ie1O|8eB!e1NhIP6AkU5dj3}x@_rV(AtXV(2J2slN2bM35Kpr{1IqT8 z=tYV)rC2%q3lWz+0k@)4h$2(S*~i$<+-qx@2gm2N(&V~tf1L?bLQFP-c{iHa%>0%m z=cO61T_yIICWZvronotL=(&jxAXb3f2$s~@n!gVxv}rY`USZ9>=YNKI^&o}*T5@5- zUx>$Mcd{0Y{j6V7s38q`?eKq^$^s`lz8zp1Yoed=n_7}<8iXE<7mP(X&(K+UNIysz zMMHBKF}V~6INsd&TOn4EMlQ2H&yM`r=^+y@#@cJe;mjlVwmCAeUc-8covPe#LjK!7 z&w>c1rFbs_%V|bdAHlTDS1|4qe@FuZ5!y~{FO58~f|+P0A8XlfG`ho;wWU}x_z}n- zX532np8so<^MB1^KReh*(nattiB0fzWWEDmRRq0OnB=*{?%FOs5g!UKfeGfPm@EO! zl)$%(^+QH3+t6?ty-)0*@t5fJzmAA~CE&FR*8O$6R$l)^6WL3_SB%xf*BUn+^Zw}6 z1$S9qU5czkw+kaOzI@<9ivJ~NGEK!H?s5>4wPfg*5yK$%Adr#yDu`QI&!)*4tYx+B zm^H6xh3?sD?69?BL(mP&`l2;>2Aq!nIa(QMq%S$Mb^rS+Hr9-yu^BQy2!UV)iroRT zBK8ZcVkf}VS%CNnYi1GRbIFNElWh>5$GjT8W)w>e_X56v5>{O&KR$7DRQ(u~5yBXHN< z1g9<<7txw;I>W%L4A*3Wdv zI2_q-P%mm#wo?krrheIJT8cA{oFX3jlvsx&=zR$8_ad}_*T-}kSWX^v7NT*Q`pL-a zPac1G?7XFivy3OiWCMxaX45TLhhPu0S!W_1$#mKp`Vj98e=gjj-~zO=pdBF$x5f7? zYDo=yZ3&vMW~4&UkE9f!>^tKsVyksn6qFSp7R=_E&?<(;F~nv4nU}NNtzbfYPvLZ7 zR78XCS}_hs()WW5ta5=7ZEJhLL>x;eMlRG|k{y+;_2y_c5*y9@JmUsAZCK}G{3bSy ziizFI+z6@iiSA{|h!0}OiohGlkiBHM7F*yOf>m%FL8Lm#MOn*wnBXN#;;&PjE(ni< z(~0>v^Pe`I1n_&&bXnrEMr@)Nnx8>gUidS~ALThFKEmDv6dVgNsJ$X7H{{betI*I9 zxWO44Igv6H+KaEK?J^|=httFpVjY-A1>3_NO}sDus$gQoI?JO9; zJiQSfG50}eErPx@m&G`dS%)T;37;=JSqR5#6PV{B-WdNL8py%$Wi5NlsL#414Xu=x zuK#={c^JPCJWk=*_+$lH%XTuxGmq(B@m>#-%fSC;>jX4$hE2?X)0*NpDO?v{Ro3Sy z=CygYsX#C)&g8^Z-)%=p_(fuUgd>27@aBdf3uTD|h;61&K}I$T@rOZfo5a_t#7>|w zlAIb8%>brjEo)K{yKWGezE6vX>)_Mb1{@Gdb22 z2(5)D`$6N=Xto0}*&Nm-;Pyi|4RhHl^qMjM>#ocGe;Iibj^B2jLb!uG<0X~}!3&H) zVy9{Bk8u`TW3P#gfZvQF1t^fx3N5wMxo7CE&X;IBV1z-pi1Ky*$yr3RCh{4p5QV}( z97M5`_)Qf|2<<09n)LC$ zg|(Ims?a!_FM%w}Psgz-5R>thF%q#WH1$EvF+W71KbDh>_%7Ss74ubr6NPyWTPH!U z4>>7lthU|{G8n>a5~mV~i|Bdq4%pgu?rDSjGQQ1lWxp9!;pMg_6H>GT@#Dk;m=~m2 z2a3oN!;`JY_W_*AsK)rkdW_4uZAb&Mq>%Gi5v_l)?oE-r*34jdms$I`{LR$v4=UTYyfjTg0?aWt~ZeNtTi=@9EfkllhXjCmdAb0BTB z1jS;}L?7dQr{FFtFy5LIXCcKC+B%<|sQkg;mLT_!=?%0d-|DH;9Y$YBXHBps0--3- zm4s)E>on4j6KRU?pc+7=EAR&1C2~#>pAe*pudGMAcgK(S@CVRH<{-^H#TUy?%vIcu z`Xe0`M5JC2A!;~xO-S=Mh|~oqk-U>4=fLtoPDq^3R%j@(nsNlOJIx5pJDjWSsO@W5&0RU<4<-n4JdZQ{43$k#P3fdOUW%vEHdKx!TN}=u$+Ocqfw)V zZB+dXCFdbJ+xZh3w`F11=OD@sAw0@6b$_jlKP>y(y z^GEPvxIH`>A%B)}_Y!-K|22i*({N6j{bEgousXMGv+n&DMChi8$25^Itmo2cLIlDx zpMt;~OIFQ0%=dzy>;#Gse+_pVBN0Qk#RE=iI3*aqR^uwgqj?%|ImxZF*4EK>*6k6W z52i7p+*Y^<%~eO_GyaD(Eo;VDz-cwa7frdudzw~5^4^(#crZKpDTx=MnR(!E{R_F> zOun(xV+hS5unMsf5bH2MMbZXhs~NI5#AIQJ$0UBjn%IPf>^JKaG%|p~Rjv7R#2({s z$XH7LE}B~pcOd>#`gt%8Hx$`qM4uokTgf~HvDc6`u&!x4I)MKM;tvqtg5Y4{zE)_M z@`ycUJ&wl9Skuybs4rMaa)O$xA_?OlcBiw|j5*AwSg|k6t0VT@lA2Lmc8#L3K%+TF znBQddfLj!e?-n>=o2i3;2)S?2%SLRj`LF5u&jTEdag;-#0m)uFPSKhax<^t_>yN)E zViT-LP3D~_Ap3xCz4<*J&)24y8{Y#O&ncy=M$wq;CB&^^FOEG^=Y${_e z>&Y}zAH0PBhBZ5im|oUN0FEYaIXcDRbRvGz3Ra}ay2KN3sdK_1>>NNVq6??2JDzoj zmL!6LBvjNm4QAI0_|a2P@kl84b)YHL)~+RVog?*=a$MXtgd38xbN<214tY!7cD z@pu$25F|GS@oD(Vdm48+yD9jd_S zi=2oQ=m`!17txs4=Fvn?awfvRO5SHSR!jdvz9*t#2xKN{IpY|_VBj{!bz&_b=VKgX zo{VPuA$kXXUe*~HkMYSGdB8bC{wjRw;bli-4BSufWZx-15DZN$HNJN)Ch_PrvF$>n z-6UniH=Pb;$B30gG@=#yg1AK|^92)V_ZNPk`i^+4wAqX=m(w^iW z5WYbEiTEStUQ0+*@vPB^6c2*{Jvt@v z{yItcP^sJbwQSO?_m)lHQ$-E<5YA^vxquV3d^R_jvC-cr-i(?4KCx%?@b^g?aLV6j zP4s{fCw)dG4B6GMS;r2|17be+xtTOXYQL^+TL-)d=Nm3sK!`-XRcZxn@8Mgh?Tie8 qzS(Ddec+ojph=)_$`}DNzxuvP7P@Pb4y}9pwet%Y8!woD?EeEGg_ooN diff --git a/netbox/translations/tr/LC_MESSAGES/django.po b/netbox/translations/tr/LC_MESSAGES/django.po index ce0eeec33..f5dcbcac7 100644 --- a/netbox/translations/tr/LC_MESSAGES/django.po +++ b/netbox/translations/tr/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-21 19:48+0000\n" +"POT-Creation-Date: 2024-04-04 19:11+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" "Last-Translator: Jeremy Stretch, 2024\n" "Language-Team: Turkish (https://app.transifex.com/netbox-community/teams/178115/tr/)\n" @@ -64,8 +64,8 @@ msgid "Your preferences have been updated." msgstr "Tercihleriniz güncellendi." #: circuits/choices.py:21 dcim/choices.py:20 dcim/choices.py:102 -#: dcim/choices.py:174 dcim/choices.py:220 dcim/choices.py:1419 -#: dcim/choices.py:1495 dcim/choices.py:1545 virtualization/choices.py:20 +#: dcim/choices.py:174 dcim/choices.py:220 dcim/choices.py:1425 +#: dcim/choices.py:1501 dcim/choices.py:1551 virtualization/choices.py:20 #: virtualization/choices.py:45 vpn/choices.py:18 msgid "Planned" msgstr "Planlanan" @@ -75,8 +75,8 @@ msgid "Provisioning" msgstr "Tedarik" #: circuits/choices.py:23 dcim/choices.py:22 dcim/choices.py:103 -#: dcim/choices.py:173 dcim/choices.py:219 dcim/choices.py:1494 -#: dcim/choices.py:1544 extras/tables/tables.py:380 ipam/choices.py:31 +#: dcim/choices.py:173 dcim/choices.py:219 dcim/choices.py:1500 +#: dcim/choices.py:1550 extras/tables/tables.py:380 ipam/choices.py:31 #: ipam/choices.py:49 ipam/choices.py:69 ipam/choices.py:154 #: templates/extras/configcontext.html:26 templates/users/user.html:38 #: users/forms/bulk_edit.py:36 virtualization/choices.py:22 @@ -85,7 +85,7 @@ msgid "Active" msgstr "Aktif" #: circuits/choices.py:24 dcim/choices.py:172 dcim/choices.py:218 -#: dcim/choices.py:1493 dcim/choices.py:1546 virtualization/choices.py:24 +#: dcim/choices.py:1499 dcim/choices.py:1552 virtualization/choices.py:24 #: virtualization/choices.py:43 msgid "Offline" msgstr "Çevrim dışı" @@ -98,7 +98,7 @@ msgstr "Hazırlıktan Kaldırma" msgid "Decommissioned" msgstr "Hizmet dışı bırakıldı" -#: circuits/filtersets.py:29 circuits/filtersets.py:184 dcim/filtersets.py:124 +#: circuits/filtersets.py:29 circuits/filtersets.py:190 dcim/filtersets.py:124 #: dcim/filtersets.py:185 dcim/filtersets.py:260 dcim/filtersets.py:369 #: dcim/filtersets.py:903 dcim/filtersets.py:1207 dcim/filtersets.py:1702 #: dcim/filtersets.py:1945 dcim/filtersets.py:2003 ipam/filtersets.py:305 @@ -107,7 +107,7 @@ msgstr "Hizmet dışı bırakıldı" msgid "Region (ID)" msgstr "Bölge (ID)" -#: circuits/filtersets.py:36 circuits/filtersets.py:191 dcim/filtersets.py:130 +#: circuits/filtersets.py:36 circuits/filtersets.py:197 dcim/filtersets.py:130 #: dcim/filtersets.py:192 dcim/filtersets.py:267 dcim/filtersets.py:376 #: dcim/filtersets.py:910 dcim/filtersets.py:1214 dcim/filtersets.py:1709 #: dcim/filtersets.py:1952 dcim/filtersets.py:2010 extras/filtersets.py:414 @@ -117,7 +117,7 @@ msgstr "Bölge (ID)" msgid "Region (slug)" msgstr "Bölge (kısa ad)" -#: circuits/filtersets.py:42 circuits/filtersets.py:197 dcim/filtersets.py:198 +#: circuits/filtersets.py:42 circuits/filtersets.py:203 dcim/filtersets.py:198 #: dcim/filtersets.py:273 dcim/filtersets.py:382 dcim/filtersets.py:916 #: dcim/filtersets.py:1220 dcim/filtersets.py:1715 dcim/filtersets.py:1958 #: dcim/filtersets.py:2016 ipam/filtersets.py:318 ipam/filtersets.py:909 @@ -125,7 +125,7 @@ msgstr "Bölge (kısa ad)" msgid "Site group (ID)" msgstr "Site grubu (ID)" -#: circuits/filtersets.py:49 circuits/filtersets.py:204 dcim/filtersets.py:205 +#: circuits/filtersets.py:49 circuits/filtersets.py:210 dcim/filtersets.py:205 #: dcim/filtersets.py:280 dcim/filtersets.py:389 dcim/filtersets.py:923 #: dcim/filtersets.py:1227 dcim/filtersets.py:1722 dcim/filtersets.py:1965 #: dcim/filtersets.py:2023 extras/filtersets.py:420 ipam/filtersets.py:325 @@ -135,7 +135,7 @@ msgid "Site group (slug)" msgstr "Site grubu (kısa ad)" #: circuits/filtersets.py:54 circuits/forms/bulk_import.py:117 -#: circuits/forms/filtersets.py:47 circuits/forms/filtersets.py:171 +#: circuits/forms/filtersets.py:47 circuits/forms/filtersets.py:167 #: circuits/forms/model_forms.py:137 dcim/forms/bulk_edit.py:166 #: dcim/forms/bulk_edit.py:238 dcim/forms/bulk_edit.py:570 #: dcim/forms/bulk_edit.py:763 dcim/forms/bulk_import.py:130 @@ -158,8 +158,8 @@ msgstr "Site grubu (kısa ad)" #: ipam/forms/bulk_import.py:170 ipam/forms/bulk_import.py:437 #: ipam/forms/filtersets.py:152 ipam/forms/filtersets.py:226 #: ipam/forms/filtersets.py:417 ipam/forms/filtersets.py:470 -#: ipam/forms/model_forms.py:206 ipam/forms/model_forms.py:548 -#: ipam/forms/model_forms.py:640 ipam/tables/ip.py:244 +#: ipam/forms/model_forms.py:206 ipam/forms/model_forms.py:552 +#: ipam/forms/model_forms.py:644 ipam/tables/ip.py:244 #: ipam/tables/vlans.py:114 ipam/tables/vlans.py:216 #: templates/circuits/circuittermination_edit.html:20 #: templates/circuits/inc/circuit_termination.html:33 @@ -181,13 +181,13 @@ msgstr "Site grubu (kısa ad)" #: virtualization/forms/model_forms.py:107 #: virtualization/forms/model_forms.py:174 #: virtualization/tables/clusters.py:77 -#: virtualization/tables/virtualmachines.py:53 vpn/forms/filtersets.py:262 +#: virtualization/tables/virtualmachines.py:62 vpn/forms/filtersets.py:262 #: wireless/forms/model_forms.py:77 wireless/forms/model_forms.py:117 msgid "Site" msgstr "Site" -#: circuits/filtersets.py:60 circuits/filtersets.py:215 -#: circuits/filtersets.py:252 dcim/filtersets.py:215 dcim/filtersets.py:290 +#: circuits/filtersets.py:60 circuits/filtersets.py:221 +#: circuits/filtersets.py:258 dcim/filtersets.py:215 dcim/filtersets.py:290 #: dcim/filtersets.py:363 extras/filtersets.py:436 ipam/filtersets.py:215 #: ipam/filtersets.py:335 ipam/filtersets.py:926 #: virtualization/filtersets.py:75 virtualization/filtersets.py:203 @@ -199,33 +199,39 @@ msgstr "Site (kısa ad)" msgid "ASN (ID)" msgstr "ASN (ID)" -#: circuits/filtersets.py:87 circuits/filtersets.py:114 -#: circuits/filtersets.py:148 -msgid "Provider (ID)" -msgstr "Sağlayıcı (ID)" +#: circuits/filtersets.py:71 circuits/forms/filtersets.py:27 +#: ipam/forms/model_forms.py:158 ipam/models/asns.py:108 +#: ipam/models/asns.py:125 ipam/tables/asn.py:41 templates/ipam/asn.html:20 +msgid "ASN" +msgstr "ASN" #: circuits/filtersets.py:93 circuits/filtersets.py:120 #: circuits/filtersets.py:154 +msgid "Provider (ID)" +msgstr "Sağlayıcı (ID)" + +#: circuits/filtersets.py:99 circuits/filtersets.py:126 +#: circuits/filtersets.py:160 msgid "Provider (slug)" msgstr "Sağlayıcı (kısa ad)" -#: circuits/filtersets.py:159 +#: circuits/filtersets.py:165 msgid "Provider account (ID)" msgstr "Sağlayıcı hesabı (ID)" -#: circuits/filtersets.py:164 +#: circuits/filtersets.py:170 msgid "Provider network (ID)" msgstr "Sağlayıcı ağı (ID)" -#: circuits/filtersets.py:168 +#: circuits/filtersets.py:174 msgid "Circuit type (ID)" msgstr "Devre tipi (ID)" -#: circuits/filtersets.py:174 +#: circuits/filtersets.py:180 msgid "Circuit type (slug)" msgstr "Devre tipi (kısa ad)" -#: circuits/filtersets.py:209 circuits/filtersets.py:246 +#: circuits/filtersets.py:215 circuits/filtersets.py:252 #: dcim/filtersets.py:209 dcim/filtersets.py:284 dcim/filtersets.py:357 #: dcim/filtersets.py:927 dcim/filtersets.py:1232 dcim/filtersets.py:1727 #: dcim/filtersets.py:1969 dcim/filtersets.py:2028 ipam/filtersets.py:209 @@ -235,13 +241,13 @@ msgstr "Devre tipi (kısa ad)" msgid "Site (ID)" msgstr "Site (ID)" -#: circuits/filtersets.py:238 core/filtersets.py:73 core/filtersets.py:132 +#: circuits/filtersets.py:244 core/filtersets.py:73 core/filtersets.py:132 #: dcim/filtersets.py:640 dcim/filtersets.py:1201 dcim/filtersets.py:2076 #: extras/filtersets.py:40 extras/filtersets.py:69 extras/filtersets.py:101 #: extras/filtersets.py:140 extras/filtersets.py:168 extras/filtersets.py:195 #: extras/filtersets.py:226 extras/filtersets.py:295 extras/filtersets.py:343 #: extras/filtersets.py:403 extras/filtersets.py:562 extras/filtersets.py:604 -#: extras/filtersets.py:645 ipam/forms/model_forms.py:430 +#: extras/filtersets.py:645 ipam/forms/model_forms.py:416 #: netbox/filtersets.py:275 netbox/forms/__init__.py:23 #: netbox/forms/base.py:163 templates/htmx/object_selector.html:28 #: templates/inc/filter_list.html:53 templates/ipam/ipaddress_assign.html:32 @@ -251,7 +257,7 @@ msgstr "Site (ID)" msgid "Search" msgstr "Arama" -#: circuits/filtersets.py:242 circuits/forms/bulk_edit.py:167 +#: circuits/filtersets.py:248 circuits/forms/bulk_edit.py:167 #: circuits/forms/model_forms.py:110 circuits/forms/model_forms.py:132 #: dcim/forms/connections.py:66 templates/circuits/circuit.html:15 #: templates/dcim/inc/cable_termination.html:55 @@ -259,11 +265,11 @@ msgstr "Arama" msgid "Circuit" msgstr "Devre" -#: circuits/filtersets.py:256 +#: circuits/filtersets.py:262 msgid "ProviderNetwork (ID)" msgstr "Sağlayıcı Ağı (ID)" -#: circuits/forms/bulk_edit.py:25 circuits/forms/filtersets.py:56 +#: circuits/forms/bulk_edit.py:25 circuits/forms/filtersets.py:52 #: circuits/forms/model_forms.py:26 circuits/tables/providers.py:33 #: dcim/forms/bulk_edit.py:126 dcim/forms/filtersets.py:187 #: dcim/forms/model_forms.py:126 dcim/tables/sites.py:94 @@ -374,8 +380,8 @@ msgstr "Açıklama" #: circuits/forms/bulk_edit.py:46 circuits/forms/bulk_edit.py:68 #: circuits/forms/bulk_edit.py:118 circuits/forms/bulk_import.py:35 #: circuits/forms/bulk_import.py:50 circuits/forms/bulk_import.py:76 -#: circuits/forms/filtersets.py:70 circuits/forms/filtersets.py:88 -#: circuits/forms/filtersets.py:116 circuits/forms/filtersets.py:131 +#: circuits/forms/filtersets.py:66 circuits/forms/filtersets.py:84 +#: circuits/forms/filtersets.py:112 circuits/forms/filtersets.py:127 #: circuits/forms/model_forms.py:32 circuits/forms/model_forms.py:44 #: circuits/forms/model_forms.py:58 circuits/forms/model_forms.py:92 #: circuits/tables/circuits.py:55 circuits/tables/providers.py:72 @@ -387,18 +393,18 @@ msgstr "Açıklama" msgid "Provider" msgstr "Sağlayıcı" -#: circuits/forms/bulk_edit.py:75 circuits/forms/filtersets.py:91 +#: circuits/forms/bulk_edit.py:75 circuits/forms/filtersets.py:87 #: templates/circuits/providernetwork.html:31 msgid "Service ID" msgstr "Servis ID" -#: circuits/forms/bulk_edit.py:95 circuits/forms/filtersets.py:107 +#: circuits/forms/bulk_edit.py:95 circuits/forms/filtersets.py:103 #: dcim/forms/bulk_edit.py:204 dcim/forms/bulk_edit.py:500 #: dcim/forms/bulk_edit.py:694 dcim/forms/bulk_edit.py:1063 #: dcim/forms/bulk_edit.py:1090 dcim/forms/bulk_edit.py:1562 #: dcim/forms/filtersets.py:977 dcim/forms/filtersets.py:1353 -#: dcim/forms/filtersets.py:1374 dcim/tables/devices.py:722 -#: dcim/tables/devices.py:782 dcim/tables/devices.py:1009 +#: dcim/forms/filtersets.py:1374 dcim/tables/devices.py:726 +#: dcim/tables/devices.py:786 dcim/tables/devices.py:1013 #: dcim/tables/devicetypes.py:245 dcim/tables/devicetypes.py:260 #: dcim/tables/racks.py:32 extras/forms/bulk_edit.py:259 #: extras/tables/tables.py:328 templates/circuits/circuittype.html:33 @@ -410,7 +416,7 @@ msgid "Color" msgstr "Renk" #: circuits/forms/bulk_edit.py:113 circuits/forms/bulk_import.py:89 -#: circuits/forms/filtersets.py:126 core/forms/bulk_edit.py:17 +#: circuits/forms/filtersets.py:122 core/forms/bulk_edit.py:17 #: core/forms/filtersets.py:29 core/tables/data.py:20 core/tables/jobs.py:18 #: dcim/forms/bulk_edit.py:281 dcim/forms/bulk_edit.py:672 #: dcim/forms/bulk_edit.py:811 dcim/forms/bulk_edit.py:879 @@ -429,7 +435,7 @@ msgstr "Renk" #: dcim/forms/filtersets.py:1253 dcim/forms/filtersets.py:1348 #: dcim/forms/filtersets.py:1369 dcim/forms/object_import.py:89 #: dcim/forms/object_import.py:118 dcim/forms/object_import.py:150 -#: dcim/tables/devices.py:211 dcim/tables/devices.py:838 +#: dcim/tables/devices.py:211 dcim/tables/devices.py:842 #: dcim/tables/power.py:77 extras/forms/bulk_import.py:39 #: extras/tables/tables.py:278 extras/tables/tables.py:350 #: extras/tables/tables.py:448 netbox/tables/tables.py:234 @@ -454,12 +460,12 @@ msgid "Type" msgstr "Tür" #: circuits/forms/bulk_edit.py:123 circuits/forms/bulk_import.py:82 -#: circuits/forms/filtersets.py:139 circuits/forms/model_forms.py:97 +#: circuits/forms/filtersets.py:135 circuits/forms/model_forms.py:97 msgid "Provider account" msgstr "Sağlayıcı hesabı" #: circuits/forms/bulk_edit.py:131 circuits/forms/bulk_import.py:95 -#: circuits/forms/filtersets.py:150 core/forms/filtersets.py:34 +#: circuits/forms/filtersets.py:146 core/forms/filtersets.py:34 #: core/forms/filtersets.py:75 core/tables/data.py:23 core/tables/jobs.py:26 #: dcim/forms/bulk_edit.py:104 dcim/forms/bulk_edit.py:179 #: dcim/forms/bulk_edit.py:260 dcim/forms/bulk_edit.py:593 @@ -473,8 +479,8 @@ msgstr "Sağlayıcı hesabı" #: dcim/forms/filtersets.py:281 dcim/forms/filtersets.py:726 #: dcim/forms/filtersets.py:835 dcim/forms/filtersets.py:871 #: dcim/forms/filtersets.py:972 dcim/forms/filtersets.py:1083 -#: dcim/tables/devices.py:173 dcim/tables/devices.py:841 -#: dcim/tables/devices.py:1069 dcim/tables/modules.py:69 +#: dcim/tables/devices.py:173 dcim/tables/devices.py:845 +#: dcim/tables/devices.py:1073 dcim/tables/modules.py:69 #: dcim/tables/power.py:74 dcim/tables/racks.py:66 dcim/tables/sites.py:82 #: dcim/tables/sites.py:133 ipam/forms/bulk_edit.py:240 #: ipam/forms/bulk_edit.py:289 ipam/forms/bulk_edit.py:337 @@ -482,7 +488,7 @@ msgstr "Sağlayıcı hesabı" #: ipam/forms/bulk_import.py:256 ipam/forms/bulk_import.py:292 #: ipam/forms/bulk_import.py:458 ipam/forms/filtersets.py:205 #: ipam/forms/filtersets.py:270 ipam/forms/filtersets.py:341 -#: ipam/forms/filtersets.py:482 ipam/forms/model_forms.py:449 +#: ipam/forms/filtersets.py:482 ipam/forms/model_forms.py:435 #: ipam/tables/ip.py:236 ipam/tables/ip.py:309 ipam/tables/ip.py:359 #: ipam/tables/ip.py:421 ipam/tables/ip.py:448 ipam/tables/vlans.py:122 #: ipam/tables/vlans.py:227 templates/circuits/circuit.html:35 @@ -503,7 +509,7 @@ msgstr "Sağlayıcı hesabı" #: virtualization/forms/bulk_import.py:80 #: virtualization/forms/filtersets.py:61 #: virtualization/forms/filtersets.py:156 virtualization/tables/clusters.py:74 -#: virtualization/tables/virtualmachines.py:50 vpn/forms/bulk_edit.py:38 +#: virtualization/tables/virtualmachines.py:59 vpn/forms/bulk_edit.py:38 #: vpn/forms/bulk_import.py:37 vpn/forms/filtersets.py:46 #: vpn/tables/tunnels.py:48 wireless/forms/bulk_edit.py:42 #: wireless/forms/bulk_edit.py:104 wireless/forms/bulk_import.py:43 @@ -514,7 +520,7 @@ msgid "Status" msgstr "Durum" #: circuits/forms/bulk_edit.py:137 circuits/forms/bulk_import.py:100 -#: circuits/forms/filtersets.py:119 dcim/forms/bulk_edit.py:120 +#: circuits/forms/filtersets.py:115 dcim/forms/bulk_edit.py:120 #: dcim/forms/bulk_edit.py:185 dcim/forms/bulk_edit.py:255 #: dcim/forms/bulk_edit.py:366 dcim/forms/bulk_edit.py:583 #: dcim/forms/bulk_edit.py:684 dcim/forms/bulk_edit.py:1590 @@ -572,15 +578,15 @@ msgstr "Durum" msgid "Tenant" msgstr "Kiracı" -#: circuits/forms/bulk_edit.py:142 circuits/forms/filtersets.py:174 +#: circuits/forms/bulk_edit.py:142 circuits/forms/filtersets.py:170 msgid "Install date" msgstr "Yükleme tarihi" -#: circuits/forms/bulk_edit.py:147 circuits/forms/filtersets.py:179 +#: circuits/forms/bulk_edit.py:147 circuits/forms/filtersets.py:175 msgid "Termination date" msgstr "Fesih tarihi" -#: circuits/forms/bulk_edit.py:153 circuits/forms/filtersets.py:186 +#: circuits/forms/bulk_edit.py:153 circuits/forms/filtersets.py:182 msgid "Commit rate (Kbps)" msgstr "Taahhüt oranı (Kbps)" @@ -613,7 +619,7 @@ msgstr "Atanan sağlayıcı" #: circuits/forms/bulk_import.py:70 dcim/forms/bulk_import.py:178 #: dcim/forms/bulk_import.py:388 dcim/forms/bulk_import.py:1108 -#: dcim/forms/bulk_import.py:1187 extras/forms/bulk_import.py:229 +#: dcim/forms/bulk_import.py:1187 extras/forms/bulk_import.py:235 msgid "RGB color in hexadecimal. Example:" msgstr "Onaltılık değerde RGB rengi. Örnek:" @@ -649,12 +655,12 @@ msgstr "Operasyonel durum" msgid "Assigned tenant" msgstr "Atanan kiracı" -#: circuits/forms/bulk_import.py:123 circuits/forms/filtersets.py:147 +#: circuits/forms/bulk_import.py:123 circuits/forms/filtersets.py:143 #: circuits/forms/model_forms.py:143 msgid "Provider network" msgstr "Sağlayıcı ağı" -#: circuits/forms/filtersets.py:26 circuits/forms/filtersets.py:118 +#: circuits/forms/filtersets.py:26 circuits/forms/filtersets.py:114 #: dcim/forms/bulk_edit.py:247 dcim/forms/bulk_edit.py:345 #: dcim/forms/bulk_edit.py:575 dcim/forms/bulk_edit.py:622 #: dcim/forms/bulk_edit.py:772 dcim/forms/bulk_import.py:189 @@ -678,7 +684,7 @@ msgstr "Sağlayıcı ağı" #: extras/filtersets.py:441 extras/forms/filtersets.py:328 #: ipam/forms/bulk_edit.py:456 ipam/forms/filtersets.py:168 #: ipam/forms/filtersets.py:400 ipam/forms/filtersets.py:422 -#: ipam/forms/filtersets.py:448 ipam/forms/model_forms.py:560 +#: ipam/forms/filtersets.py:448 ipam/forms/model_forms.py:564 #: templates/dcim/device.html:26 templates/dcim/device_edit.html:30 #: templates/dcim/inc/cable_termination.html:12 #: templates/dcim/location.html:27 templates/dcim/powerpanel.html:27 @@ -688,13 +694,7 @@ msgstr "Sağlayıcı ağı" msgid "Location" msgstr "Konum" -#: circuits/forms/filtersets.py:27 ipam/forms/model_forms.py:158 -#: ipam/models/asns.py:108 ipam/models/asns.py:125 ipam/tables/asn.py:41 -#: templates/ipam/asn.html:20 -msgid "ASN" -msgstr "ASN" - -#: circuits/forms/filtersets.py:28 circuits/forms/filtersets.py:120 +#: circuits/forms/filtersets.py:28 circuits/forms/filtersets.py:116 #: dcim/forms/filtersets.py:136 dcim/forms/filtersets.py:150 #: dcim/forms/filtersets.py:166 dcim/forms/filtersets.py:198 #: dcim/forms/filtersets.py:249 dcim/forms/filtersets.py:334 @@ -707,7 +707,7 @@ msgstr "ASN" msgid "Contacts" msgstr "İletişim" -#: circuits/forms/filtersets.py:33 circuits/forms/filtersets.py:157 +#: circuits/forms/filtersets.py:33 circuits/forms/filtersets.py:153 #: dcim/forms/bulk_edit.py:110 dcim/forms/bulk_edit.py:222 #: dcim/forms/bulk_edit.py:747 dcim/forms/bulk_import.py:92 #: dcim/forms/filtersets.py:70 dcim/forms/filtersets.py:177 @@ -722,7 +722,7 @@ msgstr "İletişim" #: ipam/forms/bulk_edit.py:205 ipam/forms/bulk_edit.py:437 #: ipam/forms/bulk_edit.py:509 ipam/forms/filtersets.py:212 #: ipam/forms/filtersets.py:407 ipam/forms/filtersets.py:456 -#: ipam/forms/model_forms.py:532 templates/dcim/device.html:18 +#: ipam/forms/model_forms.py:536 templates/dcim/device.html:18 #: templates/dcim/rack.html:19 templates/dcim/rackreservation.html:25 #: templates/dcim/region.html:26 templates/dcim/site.html:31 #: templates/ipam/prefix.html:50 templates/ipam/vlan.html:19 @@ -732,7 +732,7 @@ msgstr "İletişim" msgid "Region" msgstr "Bölge" -#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:162 +#: circuits/forms/filtersets.py:38 circuits/forms/filtersets.py:158 #: dcim/forms/bulk_edit.py:230 dcim/forms/bulk_edit.py:755 #: dcim/forms/filtersets.py:75 dcim/forms/filtersets.py:182 #: dcim/forms/filtersets.py:208 dcim/forms/filtersets.py:269 @@ -742,19 +742,15 @@ msgstr "Bölge" #: extras/filtersets.py:425 ipam/forms/bulk_edit.py:210 #: ipam/forms/bulk_edit.py:444 ipam/forms/bulk_edit.py:514 #: ipam/forms/filtersets.py:217 ipam/forms/filtersets.py:412 -#: ipam/forms/filtersets.py:461 ipam/forms/model_forms.py:545 +#: ipam/forms/filtersets.py:461 ipam/forms/model_forms.py:549 #: virtualization/forms/bulk_edit.py:85 virtualization/forms/filtersets.py:68 #: virtualization/forms/filtersets.py:134 #: virtualization/forms/model_forms.py:101 msgid "Site group" msgstr "Site grubu" -#: circuits/forms/filtersets.py:51 -msgid "ASN (legacy)" -msgstr "ASN (eski)" - -#: circuits/forms/filtersets.py:65 circuits/forms/filtersets.py:83 -#: circuits/forms/filtersets.py:102 circuits/forms/filtersets.py:117 +#: circuits/forms/filtersets.py:61 circuits/forms/filtersets.py:79 +#: circuits/forms/filtersets.py:98 circuits/forms/filtersets.py:113 #: core/forms/filtersets.py:63 dcim/forms/bulk_edit.py:718 #: dcim/forms/filtersets.py:164 dcim/forms/filtersets.py:196 #: dcim/forms/filtersets.py:825 dcim/forms/filtersets.py:920 @@ -780,7 +776,7 @@ msgstr "ASN (eski)" msgid "Attributes" msgstr "Öznitellikler" -#: circuits/forms/filtersets.py:73 circuits/tables/circuits.py:60 +#: circuits/forms/filtersets.py:69 circuits/tables/circuits.py:60 #: circuits/tables/providers.py:66 templates/circuits/circuit.html:23 #: templates/circuits/provideraccount.html:25 msgid "Account" @@ -801,7 +797,7 @@ msgstr "Devre Tipi" #: dcim/models/device_component_templates.py:491 #: dcim/models/device_component_templates.py:591 #: dcim/models/device_components.py:976 dcim/models/device_components.py:1050 -#: dcim/models/device_components.py:1166 dcim/models/devices.py:467 +#: dcim/models/device_components.py:1166 dcim/models/devices.py:469 #: dcim/models/racks.py:43 extras/models/tags.py:28 msgid "color" msgstr "renk" @@ -823,8 +819,8 @@ msgid "Unique circuit ID" msgstr "Benzersiz devre ID" #: circuits/models/circuits.py:67 core/models/data.py:55 -#: core/models/jobs.py:85 dcim/models/cables.py:49 dcim/models/devices.py:641 -#: dcim/models/devices.py:1165 dcim/models/devices.py:1374 +#: core/models/jobs.py:85 dcim/models/cables.py:49 dcim/models/devices.py:643 +#: dcim/models/devices.py:1170 dcim/models/devices.py:1379 #: dcim/models/power.py:95 dcim/models/racks.py:97 dcim/models/sites.py:154 #: dcim/models/sites.py:266 ipam/models/ip.py:252 ipam/models/ip.py:521 #: ipam/models/ip.py:729 ipam/models/vlans.py:175 @@ -903,7 +899,7 @@ msgstr "Yama paneli kimliği ve bağlantı noktası numaraları" #: extras/models/models.py:541 extras/models/staging.py:31 #: extras/models/tags.py:32 netbox/models/__init__.py:109 #: netbox/models/__init__.py:144 netbox/models/__init__.py:190 -#: users/models.py:273 users/models.py:348 +#: users/models.py:274 users/models.py:353 #: virtualization/models/virtualmachines.py:282 msgid "description" msgstr "açıklama" @@ -930,8 +926,8 @@ msgstr "Devre sonlandırma hem siteye hem de sağlayıcı ağına bağlanamaz." #: circuits/models/providers.py:22 circuits/models/providers.py:66 #: circuits/models/providers.py:104 core/models/data.py:42 #: core/models/jobs.py:46 dcim/models/device_component_templates.py:43 -#: dcim/models/device_components.py:54 dcim/models/devices.py:581 -#: dcim/models/devices.py:1305 dcim/models/devices.py:1370 +#: dcim/models/device_components.py:54 dcim/models/devices.py:583 +#: dcim/models/devices.py:1310 dcim/models/devices.py:1375 #: dcim/models/power.py:39 dcim/models/power.py:91 dcim/models/racks.py:62 #: dcim/models/sites.py:138 extras/models/configs.py:36 #: extras/models/configs.py:215 extras/models/customfields.py:89 @@ -944,7 +940,7 @@ msgstr "Devre sonlandırma hem siteye hem de sağlayıcı ağına bağlanamaz." #: ipam/models/vrfs.py:79 netbox/models/__init__.py:136 #: netbox/models/__init__.py:180 tenancy/models/contacts.py:64 #: tenancy/models/tenants.py:20 tenancy/models/tenants.py:45 -#: users/models.py:344 virtualization/models/clusters.py:57 +#: users/models.py:349 virtualization/models/clusters.py:57 #: virtualization/models/virtualmachines.py:70 #: virtualization/models/virtualmachines.py:272 vpn/models/crypto.py:24 #: vpn/models/crypto.py:71 vpn/models/crypto.py:131 vpn/models/crypto.py:183 @@ -1002,13 +998,13 @@ msgstr "sağlayıcı ağları" #: core/tables/data.py:16 core/tables/jobs.py:14 dcim/forms/filtersets.py:60 #: dcim/forms/object_create.py:42 dcim/tables/devices.py:88 #: dcim/tables/devices.py:125 dcim/tables/devices.py:167 -#: dcim/tables/devices.py:318 dcim/tables/devices.py:400 -#: dcim/tables/devices.py:444 dcim/tables/devices.py:496 -#: dcim/tables/devices.py:548 dcim/tables/devices.py:668 -#: dcim/tables/devices.py:749 dcim/tables/devices.py:799 -#: dcim/tables/devices.py:865 dcim/tables/devices.py:980 -#: dcim/tables/devices.py:1000 dcim/tables/devices.py:1029 -#: dcim/tables/devices.py:1059 dcim/tables/devicetypes.py:32 +#: dcim/tables/devices.py:322 dcim/tables/devices.py:404 +#: dcim/tables/devices.py:448 dcim/tables/devices.py:500 +#: dcim/tables/devices.py:552 dcim/tables/devices.py:672 +#: dcim/tables/devices.py:753 dcim/tables/devices.py:803 +#: dcim/tables/devices.py:869 dcim/tables/devices.py:984 +#: dcim/tables/devices.py:1004 dcim/tables/devices.py:1033 +#: dcim/tables/devices.py:1063 dcim/tables/devicetypes.py:32 #: dcim/tables/power.py:22 dcim/tables/power.py:62 dcim/tables/racks.py:23 #: dcim/tables/racks.py:53 dcim/tables/sites.py:24 dcim/tables/sites.py:51 #: dcim/tables/sites.py:78 dcim/tables/sites.py:125 @@ -1074,9 +1070,9 @@ msgstr "sağlayıcı ağları" #: virtualization/forms/object_create.py:23 #: virtualization/tables/clusters.py:17 virtualization/tables/clusters.py:39 #: virtualization/tables/clusters.py:62 -#: virtualization/tables/virtualmachines.py:45 -#: virtualization/tables/virtualmachines.py:119 -#: virtualization/tables/virtualmachines.py:172 vpn/tables/crypto.py:18 +#: virtualization/tables/virtualmachines.py:54 +#: virtualization/tables/virtualmachines.py:132 +#: virtualization/tables/virtualmachines.py:185 vpn/tables/crypto.py:18 #: vpn/tables/crypto.py:57 vpn/tables/crypto.py:93 vpn/tables/crypto.py:129 #: vpn/tables/crypto.py:158 vpn/tables/l2vpn.py:23 vpn/tables/tunnels.py:18 #: vpn/tables/tunnels.py:40 wireless/tables/wirelesslan.py:18 @@ -1111,7 +1107,7 @@ msgstr "Taahhüt Oranı" #: circuits/tables/circuits.py:75 circuits/tables/providers.py:48 #: circuits/tables/providers.py:82 circuits/tables/providers.py:107 -#: dcim/tables/devices.py:1042 dcim/tables/devicetypes.py:92 +#: dcim/tables/devices.py:1046 dcim/tables/devicetypes.py:92 #: dcim/tables/modules.py:29 dcim/tables/modules.py:72 dcim/tables/power.py:39 #: dcim/tables/power.py:96 dcim/tables/racks.py:76 dcim/tables/racks.py:156 #: dcim/tables/sites.py:103 extras/forms/bulk_edit.py:320 @@ -1123,7 +1119,7 @@ msgstr "Taahhüt Oranı" #: templates/inc/panels/comments.html:6 tenancy/tables/contacts.py:68 #: tenancy/tables/tenants.py:46 utilities/forms/fields/fields.py:29 #: virtualization/tables/clusters.py:91 -#: virtualization/tables/virtualmachines.py:68 vpn/tables/crypto.py:37 +#: virtualization/tables/virtualmachines.py:81 vpn/tables/crypto.py:37 #: vpn/tables/crypto.py:74 vpn/tables/crypto.py:109 vpn/tables/crypto.py:140 #: vpn/tables/crypto.py:173 vpn/tables/l2vpn.py:37 vpn/tables/tunnels.py:61 #: wireless/tables/wirelesslan.py:27 wireless/tables/wirelesslan.py:58 @@ -1160,7 +1156,7 @@ msgid "Completed" msgstr "Tamamlandı" #: core/choices.py:22 core/choices.py:59 dcim/choices.py:176 -#: dcim/choices.py:222 dcim/choices.py:1496 extras/choices.py:212 +#: dcim/choices.py:222 dcim/choices.py:1502 extras/choices.py:212 #: virtualization/choices.py:47 msgid "Failed" msgstr "Başarısız" @@ -1242,7 +1238,7 @@ msgstr "Veri kaynağı (isim)" #: core/forms/bulk_edit.py:24 core/forms/filtersets.py:39 #: core/tables/data.py:26 dcim/forms/bulk_edit.py:1012 #: dcim/forms/bulk_edit.py:1285 dcim/forms/filtersets.py:1270 -#: dcim/tables/devices.py:573 dcim/tables/devicetypes.py:221 +#: dcim/tables/devices.py:577 dcim/tables/devicetypes.py:221 #: extras/forms/bulk_edit.py:97 extras/forms/bulk_edit.py:161 #: extras/forms/bulk_edit.py:220 extras/forms/filtersets.py:119 #: extras/forms/filtersets.py:206 extras/forms/filtersets.py:267 @@ -1379,7 +1375,7 @@ msgstr "" msgid "Rack Elevations" msgstr "Raf Yükseltmeleri" -#: core/forms/model_forms.py:148 dcim/choices.py:1407 +#: core/forms/model_forms.py:148 dcim/choices.py:1413 #: dcim/forms/bulk_edit.py:859 dcim/forms/bulk_edit.py:1242 #: dcim/forms/bulk_edit.py:1260 dcim/tables/racks.py:89 #: netbox/navigation/menu.py:276 netbox/navigation/menu.py:280 @@ -1440,7 +1436,7 @@ msgstr " (varsayılan)" #: core/models/config.py:18 core/models/data.py:282 core/models/files.py:27 #: core/models/jobs.py:50 extras/models/models.py:760 -#: netbox/models/features.py:52 users/models.py:248 +#: netbox/models/features.py:52 users/models.py:249 msgid "created" msgstr "oluşturulan" @@ -1498,7 +1494,7 @@ msgstr "URL" #: core/models/data.py:62 dcim/models/device_component_templates.py:392 #: dcim/models/device_components.py:513 extras/models/models.py:88 -#: extras/models/models.py:331 extras/models/models.py:556 users/models.py:353 +#: extras/models/models.py:331 extras/models/models.py:556 users/models.py:358 msgid "enabled" msgstr "etkin" @@ -1714,7 +1710,7 @@ msgid "Staging" msgstr "Sahneleme" #: dcim/choices.py:23 dcim/choices.py:178 dcim/choices.py:223 -#: dcim/choices.py:1420 virtualization/choices.py:23 +#: dcim/choices.py:1426 virtualization/choices.py:23 #: virtualization/choices.py:48 msgid "Decommissioning" msgstr "Hizmetten çıkarma" @@ -1774,7 +1770,7 @@ msgstr "Kullanımdan kaldırıldı" msgid "Millimeters" msgstr "Milimetre" -#: dcim/choices.py:115 dcim/choices.py:1442 +#: dcim/choices.py:115 dcim/choices.py:1448 msgid "Inches" msgstr "İnç" @@ -1786,8 +1782,8 @@ msgstr "İnç" #: dcim/forms/filtersets.py:226 dcim/forms/model_forms.py:73 #: dcim/forms/model_forms.py:94 dcim/forms/model_forms.py:172 #: dcim/forms/model_forms.py:962 dcim/forms/model_forms.py:1303 -#: dcim/forms/object_import.py:181 dcim/tables/devices.py:676 -#: dcim/tables/devices.py:960 extras/tables/tables.py:181 +#: dcim/forms/object_import.py:181 dcim/tables/devices.py:680 +#: dcim/tables/devices.py:964 extras/tables/tables.py:181 #: ipam/tables/fhrp.py:59 ipam/tables/ip.py:374 ipam/tables/services.py:44 #: templates/dcim/interface.html:105 templates/dcim/interface.html:321 #: templates/dcim/location.html:44 templates/dcim/region.html:38 @@ -1800,7 +1796,7 @@ msgstr "İnç" #: tenancy/forms/bulk_import.py:58 tenancy/forms/model_forms.py:24 #: tenancy/forms/model_forms.py:69 virtualization/forms/bulk_edit.py:206 #: virtualization/forms/bulk_import.py:151 -#: virtualization/tables/virtualmachines.py:142 wireless/forms/bulk_edit.py:23 +#: virtualization/tables/virtualmachines.py:155 wireless/forms/bulk_edit.py:23 #: wireless/forms/bulk_import.py:21 wireless/forms/model_forms.py:20 msgid "Parent" msgstr "Ebeveyn" @@ -1849,7 +1845,7 @@ msgstr "Sağdan sola" msgid "Side to rear" msgstr "Yandan arkaya" -#: dcim/choices.py:198 dcim/choices.py:1215 +#: dcim/choices.py:198 dcim/choices.py:1221 msgid "Passive" msgstr "Pasif" @@ -1877,8 +1873,8 @@ msgstr "Uluslararası/ITA" msgid "Proprietary" msgstr "Tescilli" -#: dcim/choices.py:534 dcim/choices.py:764 dcim/choices.py:1131 -#: dcim/choices.py:1133 dcim/choices.py:1338 dcim/choices.py:1340 +#: dcim/choices.py:534 dcim/choices.py:764 dcim/choices.py:1137 +#: dcim/choices.py:1139 dcim/choices.py:1344 dcim/choices.py:1346 #: netbox/navigation/menu.py:188 msgid "Other" msgstr "Diğer" @@ -1891,177 +1887,177 @@ msgstr "ITA/Uluslararası" msgid "Physical" msgstr "Fiziksel" -#: dcim/choices.py:795 dcim/choices.py:949 +#: dcim/choices.py:795 dcim/choices.py:952 msgid "Virtual" msgstr "Sanal" -#: dcim/choices.py:796 dcim/choices.py:1019 dcim/forms/bulk_edit.py:1398 +#: dcim/choices.py:796 dcim/choices.py:1022 dcim/forms/bulk_edit.py:1398 #: dcim/forms/filtersets.py:1233 dcim/forms/model_forms.py:888 #: dcim/forms/model_forms.py:1197 netbox/navigation/menu.py:128 #: netbox/navigation/menu.py:132 templates/dcim/interface.html:217 msgid "Wireless" msgstr "Kablosuz" -#: dcim/choices.py:947 +#: dcim/choices.py:950 msgid "Virtual interfaces" msgstr "Sanal arayüzler" -#: dcim/choices.py:950 dcim/forms/bulk_edit.py:1295 +#: dcim/choices.py:953 dcim/forms/bulk_edit.py:1295 #: dcim/forms/bulk_import.py:785 dcim/forms/model_forms.py:876 -#: dcim/tables/devices.py:680 templates/dcim/interface.html:109 +#: dcim/tables/devices.py:684 templates/dcim/interface.html:109 #: templates/virtualization/vminterface.html:46 #: virtualization/forms/bulk_edit.py:211 #: virtualization/forms/bulk_import.py:158 -#: virtualization/tables/virtualmachines.py:146 +#: virtualization/tables/virtualmachines.py:159 msgid "Bridge" msgstr "Köprü" -#: dcim/choices.py:951 +#: dcim/choices.py:954 msgid "Link Aggregation Group (LAG)" msgstr "Bağlantı Toplama Grubu (LAG)" -#: dcim/choices.py:955 +#: dcim/choices.py:958 msgid "Ethernet (fixed)" msgstr "Ethernet (sabit)" -#: dcim/choices.py:969 +#: dcim/choices.py:972 msgid "Ethernet (modular)" msgstr "Ethernet (modüler)" -#: dcim/choices.py:1005 +#: dcim/choices.py:1008 msgid "Ethernet (backplane)" msgstr "Ethernet (arka panel)" -#: dcim/choices.py:1033 +#: dcim/choices.py:1036 msgid "Cellular" msgstr "Hücresel" -#: dcim/choices.py:1080 dcim/forms/filtersets.py:302 +#: dcim/choices.py:1086 dcim/forms/filtersets.py:302 #: dcim/forms/filtersets.py:736 dcim/forms/filtersets.py:876 #: dcim/forms/filtersets.py:1426 templates/dcim/inventoryitem.html:53 #: templates/dcim/virtualchassis_edit.html:55 msgid "Serial" msgstr "Seri" -#: dcim/choices.py:1095 +#: dcim/choices.py:1101 msgid "Coaxial" msgstr "Koaksiyel" -#: dcim/choices.py:1112 +#: dcim/choices.py:1118 msgid "Stacking" msgstr "İstifleme" -#: dcim/choices.py:1162 +#: dcim/choices.py:1168 msgid "Half" msgstr "Yarım" -#: dcim/choices.py:1163 +#: dcim/choices.py:1169 msgid "Full" msgstr "Dolu" -#: dcim/choices.py:1164 netbox/preferences.py:29 wireless/choices.py:480 +#: dcim/choices.py:1170 netbox/preferences.py:29 wireless/choices.py:480 msgid "Auto" msgstr "Oto" -#: dcim/choices.py:1175 +#: dcim/choices.py:1181 msgid "Access" msgstr "Erişim" -#: dcim/choices.py:1176 ipam/tables/vlans.py:168 ipam/tables/vlans.py:213 +#: dcim/choices.py:1182 ipam/tables/vlans.py:168 ipam/tables/vlans.py:213 #: templates/dcim/inc/interface_vlans_table.html:7 msgid "Tagged" msgstr "Etiketlenmiş" -#: dcim/choices.py:1177 +#: dcim/choices.py:1183 msgid "Tagged (All)" msgstr "Etiketlenmiş (Tümü)" -#: dcim/choices.py:1206 +#: dcim/choices.py:1212 msgid "IEEE Standard" msgstr "IEEE Standardı" -#: dcim/choices.py:1217 +#: dcim/choices.py:1223 msgid "Passive 24V (2-pair)" msgstr "Pasif 24V (2 çift)" -#: dcim/choices.py:1218 +#: dcim/choices.py:1224 msgid "Passive 24V (4-pair)" msgstr "Pasif 24V (4 çift)" -#: dcim/choices.py:1219 +#: dcim/choices.py:1225 msgid "Passive 48V (2-pair)" msgstr "Pasif 48V (2 çift)" -#: dcim/choices.py:1220 +#: dcim/choices.py:1226 msgid "Passive 48V (4-pair)" msgstr "Pasif 48V (4 çift)" -#: dcim/choices.py:1282 dcim/choices.py:1378 +#: dcim/choices.py:1288 dcim/choices.py:1384 msgid "Copper" msgstr "Bakır" -#: dcim/choices.py:1305 +#: dcim/choices.py:1311 msgid "Fiber Optic" msgstr "Fiber Optik" -#: dcim/choices.py:1394 +#: dcim/choices.py:1400 msgid "Fiber" msgstr "Elyaf" -#: dcim/choices.py:1418 dcim/forms/filtersets.py:1140 +#: dcim/choices.py:1424 dcim/forms/filtersets.py:1140 msgid "Connected" msgstr "Bağlı" -#: dcim/choices.py:1437 +#: dcim/choices.py:1443 msgid "Kilometers" msgstr "Kilometre" -#: dcim/choices.py:1438 templates/dcim/cable_trace.html:62 +#: dcim/choices.py:1444 templates/dcim/cable_trace.html:62 msgid "Meters" msgstr "Sayaçlar" -#: dcim/choices.py:1439 +#: dcim/choices.py:1445 msgid "Centimeters" msgstr "Santimetre" -#: dcim/choices.py:1440 +#: dcim/choices.py:1446 msgid "Miles" msgstr "Mil" -#: dcim/choices.py:1441 templates/dcim/cable_trace.html:63 +#: dcim/choices.py:1447 templates/dcim/cable_trace.html:63 msgid "Feet" msgstr "Ayaklar" -#: dcim/choices.py:1457 templates/dcim/device.html:332 +#: dcim/choices.py:1463 templates/dcim/device.html:332 #: templates/dcim/rack.html:157 msgid "Kilograms" msgstr "Kilogram" -#: dcim/choices.py:1458 +#: dcim/choices.py:1464 msgid "Grams" msgstr "Gramlar" -#: dcim/choices.py:1459 templates/dcim/rack.html:158 +#: dcim/choices.py:1465 templates/dcim/rack.html:158 msgid "Pounds" msgstr "Pound'lar" -#: dcim/choices.py:1460 +#: dcim/choices.py:1466 msgid "Ounces" msgstr "ons" -#: dcim/choices.py:1506 tenancy/choices.py:17 +#: dcim/choices.py:1512 tenancy/choices.py:17 msgid "Primary" msgstr "Birincil" -#: dcim/choices.py:1507 +#: dcim/choices.py:1513 msgid "Redundant" msgstr "Yedekli" -#: dcim/choices.py:1528 +#: dcim/choices.py:1534 msgid "Single phase" msgstr "Tek fazlı" -#: dcim/choices.py:1529 +#: dcim/choices.py:1535 msgid "Three-phase" msgstr "Üç fazlı" @@ -2338,7 +2334,7 @@ msgid "Virtual Chassis (ID)" msgstr "Sanal Kasa (ID)" #: dcim/filtersets.py:1303 dcim/forms/filtersets.py:106 -#: dcim/tables/devices.py:235 netbox/navigation/menu.py:67 +#: dcim/tables/devices.py:239 netbox/navigation/menu.py:67 #: templates/dcim/device.html:123 templates/dcim/device_edit.html:93 #: templates/dcim/virtualchassis.html:20 #: templates/dcim/virtualchassis_add.html:8 @@ -2362,7 +2358,7 @@ msgstr "Atanmış VID" #: dcim/filtersets.py:1448 dcim/forms/bulk_edit.py:1374 #: dcim/forms/bulk_import.py:836 dcim/forms/filtersets.py:1328 #: dcim/forms/model_forms.py:1182 dcim/models/device_components.py:712 -#: dcim/tables/devices.py:642 ipam/filtersets.py:282 ipam/filtersets.py:293 +#: dcim/tables/devices.py:646 ipam/filtersets.py:282 ipam/filtersets.py:293 #: ipam/filtersets.py:449 ipam/filtersets.py:550 ipam/filtersets.py:561 #: ipam/forms/bulk_edit.py:226 ipam/forms/bulk_edit.py:281 #: ipam/forms/bulk_edit.py:323 ipam/forms/bulk_import.py:156 @@ -2370,8 +2366,8 @@ msgstr "Atanmış VID" #: ipam/forms/filtersets.py:66 ipam/forms/filtersets.py:167 #: ipam/forms/filtersets.py:295 ipam/forms/model_forms.py:59 #: ipam/forms/model_forms.py:203 ipam/forms/model_forms.py:246 -#: ipam/forms/model_forms.py:290 ipam/forms/model_forms.py:412 -#: ipam/forms/model_forms.py:426 ipam/forms/model_forms.py:440 +#: ipam/forms/model_forms.py:290 ipam/forms/model_forms.py:398 +#: ipam/forms/model_forms.py:412 ipam/forms/model_forms.py:426 #: ipam/models/ip.py:232 ipam/models/ip.py:511 ipam/models/ip.py:719 #: ipam/models/vrfs.py:62 ipam/tables/ip.py:241 ipam/tables/ip.py:306 #: ipam/tables/ip.py:356 ipam/tables/ip.py:445 @@ -2384,7 +2380,7 @@ msgstr "Atanmış VID" #: virtualization/forms/filtersets.py:220 #: virtualization/forms/model_forms.py:347 #: virtualization/models/virtualmachines.py:348 -#: virtualization/tables/virtualmachines.py:123 +#: virtualization/tables/virtualmachines.py:136 msgid "VRF" msgstr "VRF" @@ -2398,7 +2394,7 @@ msgid "L2VPN (ID)" msgstr "L2VPN (KİMLİĞİ)" #: dcim/filtersets.py:1465 dcim/forms/filtersets.py:1333 -#: dcim/tables/devices.py:590 ipam/filtersets.py:973 +#: dcim/tables/devices.py:594 ipam/filtersets.py:973 #: ipam/forms/filtersets.py:499 ipam/tables/vlans.py:133 #: templates/dcim/interface.html:94 templates/ipam/vlan.html:69 #: templates/vpn/l2vpntermination.html:15 @@ -2469,7 +2465,7 @@ msgstr "Etiketler" #: dcim/forms/bulk_create.py:112 dcim/forms/filtersets.py:1390 #: dcim/forms/model_forms.py:426 dcim/forms/model_forms.py:475 #: dcim/forms/object_create.py:196 dcim/forms/object_create.py:352 -#: dcim/tables/devices.py:198 dcim/tables/devices.py:725 +#: dcim/tables/devices.py:198 dcim/tables/devices.py:729 #: dcim/tables/devicetypes.py:242 templates/dcim/device.html:45 #: templates/dcim/device.html:129 templates/dcim/modulebay.html:35 #: templates/dcim/virtualchassis.html:59 @@ -2488,7 +2484,7 @@ msgstr "" #: dcim/forms/bulk_edit.py:115 dcim/forms/bulk_import.py:99 #: dcim/forms/model_forms.py:120 dcim/tables/sites.py:89 #: ipam/filtersets.py:936 ipam/forms/bulk_edit.py:528 -#: ipam/forms/bulk_import.py:444 ipam/forms/model_forms.py:509 +#: ipam/forms/bulk_import.py:444 ipam/forms/model_forms.py:495 #: ipam/tables/fhrp.py:67 ipam/tables/vlans.py:118 ipam/tables/vlans.py:221 #: templates/dcim/interface.html:294 templates/dcim/site.html:37 #: templates/ipam/inc/panels/fhrp_groups.html:10 templates/ipam/vlan.html:30 @@ -2538,8 +2534,8 @@ msgstr "Saat dilimi" #: dcim/forms/filtersets.py:704 dcim/forms/filtersets.py:1417 #: dcim/forms/model_forms.py:224 dcim/forms/model_forms.py:970 #: dcim/forms/model_forms.py:1311 dcim/forms/object_import.py:186 -#: dcim/tables/devices.py:202 dcim/tables/devices.py:833 -#: dcim/tables/devices.py:944 dcim/tables/devicetypes.py:300 +#: dcim/tables/devices.py:202 dcim/tables/devices.py:837 +#: dcim/tables/devices.py:948 dcim/tables/devicetypes.py:300 #: dcim/tables/racks.py:69 extras/filtersets.py:457 #: ipam/forms/bulk_edit.py:245 ipam/forms/bulk_edit.py:294 #: ipam/forms/bulk_edit.py:342 ipam/forms/bulk_edit.py:546 @@ -2548,7 +2544,7 @@ msgstr "Saat dilimi" #: ipam/forms/filtersets.py:232 ipam/forms/filtersets.py:278 #: ipam/forms/filtersets.py:346 ipam/forms/filtersets.py:490 #: ipam/forms/model_forms.py:187 ipam/forms/model_forms.py:222 -#: ipam/forms/model_forms.py:249 ipam/forms/model_forms.py:647 +#: ipam/forms/model_forms.py:249 ipam/forms/model_forms.py:651 #: ipam/tables/ip.py:257 ipam/tables/ip.py:313 ipam/tables/ip.py:363 #: ipam/tables/vlans.py:126 ipam/tables/vlans.py:230 #: templates/dcim/device.html:187 @@ -2566,7 +2562,7 @@ msgstr "Saat dilimi" #: virtualization/forms/bulk_import.py:106 #: virtualization/forms/filtersets.py:153 #: virtualization/forms/model_forms.py:198 -#: virtualization/tables/virtualmachines.py:65 vpn/forms/bulk_edit.py:86 +#: virtualization/tables/virtualmachines.py:74 vpn/forms/bulk_edit.py:86 #: vpn/forms/bulk_import.py:81 vpn/forms/filtersets.py:84 #: vpn/forms/model_forms.py:77 vpn/forms/model_forms.py:112 #: vpn/tables/tunnels.py:82 @@ -2660,7 +2656,7 @@ msgstr "Ağırlık birimi" #: dcim/forms/model_forms.py:669 dcim/forms/object_create.py:399 #: dcim/tables/devices.py:194 dcim/tables/power.py:70 dcim/tables/racks.py:148 #: ipam/forms/bulk_edit.py:464 ipam/forms/filtersets.py:427 -#: ipam/forms/model_forms.py:571 templates/dcim/device.html:30 +#: ipam/forms/model_forms.py:575 templates/dcim/device.html:30 #: templates/dcim/inc/cable_termination.html:16 #: templates/dcim/powerfeed.html:31 templates/dcim/rack.html:14 #: templates/dcim/rack/base.html:4 templates/dcim/rack_edit.html:8 @@ -2693,7 +2689,7 @@ msgstr "Donanım" #: dcim/forms/model_forms.py:334 dcim/forms/model_forms.py:374 #: dcim/forms/model_forms.py:975 dcim/forms/model_forms.py:1316 #: dcim/forms/object_import.py:192 dcim/tables/devices.py:129 -#: dcim/tables/devices.py:205 dcim/tables/devices.py:947 +#: dcim/tables/devices.py:205 dcim/tables/devices.py:951 #: dcim/tables/devicetypes.py:81 dcim/tables/devicetypes.py:304 #: dcim/tables/modules.py:20 dcim/tables/modules.py:60 #: templates/dcim/devicetype.html:17 templates/dcim/inventoryitem.html:45 @@ -2740,7 +2736,7 @@ msgstr "Aygıt Türü" msgid "Module Type" msgstr "Modül Türü" -#: dcim/forms/bulk_edit.py:506 dcim/models/devices.py:472 +#: dcim/forms/bulk_edit.py:506 dcim/models/devices.py:474 msgid "VM role" msgstr "VM rolü" @@ -2772,13 +2768,15 @@ msgstr "Aygıt rolü" #: dcim/forms/bulk_edit.py:588 dcim/forms/bulk_import.py:443 #: dcim/forms/filtersets.py:723 dcim/forms/model_forms.py:389 -#: dcim/forms/model_forms.py:448 extras/filtersets.py:468 -#: templates/dcim/device.html:191 templates/dcim/platform.html:27 +#: dcim/forms/model_forms.py:448 dcim/tables/devices.py:215 +#: extras/filtersets.py:468 templates/dcim/device.html:191 +#: templates/dcim/platform.html:27 #: templates/virtualization/virtualmachine.html:30 #: virtualization/forms/bulk_edit.py:159 #: virtualization/forms/bulk_import.py:122 #: virtualization/forms/filtersets.py:164 #: virtualization/forms/model_forms.py:206 +#: virtualization/tables/virtualmachines.py:78 msgid "Platform" msgstr "Platform" @@ -2802,16 +2800,16 @@ msgstr "Platform" #: dcim/forms/model_forms.py:760 dcim/forms/model_forms.py:1011 #: dcim/forms/model_forms.py:1460 dcim/forms/object_create.py:256 #: dcim/tables/connections.py:22 dcim/tables/connections.py:41 -#: dcim/tables/connections.py:60 dcim/tables/devices.py:314 -#: dcim/tables/devices.py:379 dcim/tables/devices.py:423 -#: dcim/tables/devices.py:468 dcim/tables/devices.py:522 -#: dcim/tables/devices.py:614 dcim/tables/devices.py:715 -#: dcim/tables/devices.py:775 dcim/tables/devices.py:825 -#: dcim/tables/devices.py:885 dcim/tables/devices.py:937 -#: dcim/tables/devices.py:1063 dcim/tables/modules.py:52 +#: dcim/tables/connections.py:60 dcim/tables/devices.py:318 +#: dcim/tables/devices.py:383 dcim/tables/devices.py:427 +#: dcim/tables/devices.py:472 dcim/tables/devices.py:526 +#: dcim/tables/devices.py:618 dcim/tables/devices.py:719 +#: dcim/tables/devices.py:779 dcim/tables/devices.py:829 +#: dcim/tables/devices.py:889 dcim/tables/devices.py:941 +#: dcim/tables/devices.py:1067 dcim/tables/modules.py:52 #: extras/forms/filtersets.py:329 ipam/forms/bulk_import.py:303 #: ipam/forms/bulk_import.py:489 ipam/forms/filtersets.py:532 -#: ipam/forms/model_forms.py:685 ipam/tables/vlans.py:176 +#: ipam/forms/model_forms.py:689 ipam/tables/vlans.py:176 #: templates/dcim/consoleport.html:23 templates/dcim/consoleserverport.html:23 #: templates/dcim/device.html:14 templates/dcim/device.html:128 #: templates/dcim/device_edit.html:10 templates/dcim/devicebay.html:23 @@ -2833,7 +2831,7 @@ msgstr "Platform" #: virtualization/forms/bulk_import.py:99 #: virtualization/forms/filtersets.py:124 #: virtualization/forms/model_forms.py:188 -#: virtualization/tables/virtualmachines.py:61 vpn/choices.py:44 +#: virtualization/tables/virtualmachines.py:70 vpn/choices.py:44 #: vpn/forms/bulk_import.py:86 vpn/forms/bulk_import.py:283 #: vpn/forms/filtersets.py:271 vpn/forms/model_forms.py:89 #: vpn/forms/model_forms.py:124 vpn/forms/model_forms.py:237 @@ -2973,7 +2971,7 @@ msgid "Wireless role" msgstr "Kablosuz rolü" #: dcim/forms/bulk_edit.py:1178 dcim/forms/model_forms.py:595 -#: dcim/forms/model_forms.py:1026 dcim/tables/devices.py:337 +#: dcim/forms/model_forms.py:1026 dcim/tables/devices.py:341 #: templates/dcim/consoleport.html:27 templates/dcim/consoleserverport.html:27 #: templates/dcim/frontport.html:27 templates/dcim/interface.html:35 #: templates/dcim/module.html:51 templates/dcim/modulebay.html:57 @@ -2982,7 +2980,7 @@ msgstr "Kablosuz rolü" msgid "Module" msgstr "Modül" -#: dcim/forms/bulk_edit.py:1305 dcim/tables/devices.py:685 +#: dcim/forms/bulk_edit.py:1305 dcim/tables/devices.py:689 #: templates/dcim/interface.html:113 msgid "LAG" msgstr "GECİKME" @@ -2994,7 +2992,7 @@ msgstr "Sanal cihaz bağlamları" #: dcim/forms/bulk_edit.py:1316 dcim/forms/bulk_import.py:659 #: dcim/forms/bulk_import.py:685 dcim/forms/filtersets.py:1163 #: dcim/forms/filtersets.py:1185 dcim/forms/filtersets.py:1258 -#: dcim/tables/devices.py:626 +#: dcim/tables/devices.py:630 #: templates/circuits/inc/circuit_termination.html:94 #: templates/dcim/consoleport.html:43 templates/dcim/consoleserverport.html:43 msgid "Speed" @@ -3019,13 +3017,13 @@ msgid "VLAN group" msgstr "VLAN grubu" #: dcim/forms/bulk_edit.py:1361 dcim/forms/model_forms.py:1164 -#: dcim/tables/devices.py:599 virtualization/forms/bulk_edit.py:247 +#: dcim/tables/devices.py:603 virtualization/forms/bulk_edit.py:247 #: virtualization/forms/model_forms.py:329 msgid "Untagged VLAN" msgstr "Etiketsiz VLAN" #: dcim/forms/bulk_edit.py:1369 dcim/forms/model_forms.py:1173 -#: dcim/tables/devices.py:605 virtualization/forms/bulk_edit.py:255 +#: dcim/tables/devices.py:609 virtualization/forms/bulk_edit.py:255 #: virtualization/forms/model_forms.py:338 msgid "Tagged VLANs" msgstr "Etiketli VLAN'lar" @@ -3035,7 +3033,7 @@ msgid "Wireless LAN group" msgstr "Kablosuz LAN grubu" #: dcim/forms/bulk_edit.py:1384 dcim/forms/model_forms.py:1151 -#: dcim/tables/devices.py:635 netbox/navigation/menu.py:134 +#: dcim/tables/devices.py:639 netbox/navigation/menu.py:134 #: templates/dcim/interface.html:289 wireless/tables/wirelesslan.py:24 msgid "Wireless LANs" msgstr "Kablosuz LAN'lar" @@ -3206,9 +3204,9 @@ msgid "Virtual chassis" msgstr "Sanal şasi" #: dcim/forms/bulk_import.py:462 dcim/forms/model_forms.py:457 -#: dcim/tables/devices.py:231 extras/filtersets.py:501 +#: dcim/tables/devices.py:235 extras/filtersets.py:501 #: extras/forms/filtersets.py:330 ipam/forms/bulk_edit.py:478 -#: ipam/forms/model_forms.py:588 templates/dcim/device.html:239 +#: ipam/forms/model_forms.py:592 templates/dcim/device.html:239 #: templates/virtualization/cluster.html:11 #: templates/virtualization/virtualmachine.html:92 #: templates/virtualization/virtualmachine.html:102 @@ -3220,7 +3218,7 @@ msgstr "Sanal şasi" #: virtualization/forms/filtersets.py:196 #: virtualization/forms/model_forms.py:82 #: virtualization/forms/model_forms.py:179 -#: virtualization/tables/virtualmachines.py:57 +#: virtualization/tables/virtualmachines.py:66 msgid "Cluster" msgstr "Küme" @@ -3401,7 +3399,7 @@ msgstr "İlgili arka bağlantı noktası" msgid "Physical medium classification" msgstr "Fiziksel ortam sınıflandırması" -#: dcim/forms/bulk_import.py:973 dcim/tables/devices.py:846 +#: dcim/forms/bulk_import.py:973 dcim/tables/devices.py:850 msgid "Installed device" msgstr "Yüklü cihaz" @@ -3489,7 +3487,7 @@ msgid "{side_upper} side termination not found: {device} {name}" msgstr "{side_upper} yan sonlandırma bulunamadı: {device} {name}" #: dcim/forms/bulk_import.py:1244 dcim/forms/model_forms.py:696 -#: dcim/tables/devices.py:1033 templates/dcim/device.html:130 +#: dcim/tables/devices.py:1037 templates/dcim/device.html:130 #: templates/dcim/virtualchassis.html:28 templates/dcim/virtualchassis.html:60 msgid "Master" msgstr "Usta" @@ -3615,7 +3613,7 @@ msgstr "işgal" #: dcim/forms/filtersets.py:1155 dcim/forms/filtersets.py:1177 #: dcim/forms/filtersets.py:1199 dcim/forms/filtersets.py:1216 -#: dcim/forms/filtersets.py:1236 dcim/tables/devices.py:372 +#: dcim/forms/filtersets.py:1236 dcim/tables/devices.py:376 #: templates/dcim/consoleport.html:59 templates/dcim/consoleserverport.html:59 #: templates/dcim/frontport.html:74 templates/dcim/interface.html:146 #: templates/dcim/powerfeed.html:118 templates/dcim/poweroutlet.html:63 @@ -3629,7 +3627,7 @@ msgid "Virtual Device Context" msgstr "Sanal Aygıt Bağlamı" #: dcim/forms/filtersets.py:1248 extras/forms/bulk_edit.py:315 -#: extras/forms/bulk_import.py:239 extras/forms/filtersets.py:479 +#: extras/forms/bulk_import.py:245 extras/forms/filtersets.py:479 #: extras/forms/model_forms.py:557 extras/tables/tables.py:487 #: templates/extras/journalentry.html:33 msgid "Kind" @@ -3661,7 +3659,7 @@ msgid "Transmit power (dBm)" msgstr "İletim gücü (dBm)" #: dcim/forms/filtersets.py:1344 dcim/forms/filtersets.py:1366 -#: dcim/tables/devices.py:344 templates/dcim/cable.html:12 +#: dcim/tables/devices.py:348 templates/dcim/cable.html:12 #: templates/dcim/cable_edit.html:46 templates/dcim/cable_trace.html:43 #: templates/dcim/frontport.html:84 #: templates/dcim/inc/connection_endpoints.html:4 @@ -3669,7 +3667,7 @@ msgstr "İletim gücü (dBm)" msgid "Cable" msgstr "Kablo" -#: dcim/forms/filtersets.py:1434 dcim/tables/devices.py:956 +#: dcim/forms/filtersets.py:1434 dcim/tables/devices.py:960 msgid "Discovered" msgstr "Keşfedildi" @@ -3716,7 +3714,7 @@ msgstr "Şasi" msgid "Device Role" msgstr "Aygıt Rolü" -#: dcim/forms/model_forms.py:428 dcim/models/devices.py:632 +#: dcim/forms/model_forms.py:428 dcim/models/devices.py:634 msgid "The lowest-numbered unit occupied by the device" msgstr "Cihazın kullandığı en düşük numaralı birim" @@ -3767,9 +3765,7 @@ msgstr "LAG arayüzü" #: templates/wireless/wirelesslink.html:10 #: templates/wireless/wirelesslink.html:49 #: virtualization/forms/model_forms.py:351 vpn/forms/bulk_import.py:297 -#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 -#: vpn/forms/model_forms.py:241 vpn/forms/model_forms.py:436 -#: vpn/forms/model_forms.py:445 vpn/tables/tunnels.py:91 +#: vpn/forms/model_forms.py:436 vpn/forms/model_forms.py:445 #: wireless/forms/model_forms.py:112 wireless/forms/model_forms.py:152 msgid "Interface" msgstr "Arayüz" @@ -3846,7 +3842,7 @@ msgstr "" "bekleniyor." #: dcim/forms/object_create.py:109 dcim/forms/object_create.py:270 -#: dcim/tables/devices.py:281 +#: dcim/tables/devices.py:285 msgid "Rear ports" msgstr "Arka bağlantı noktaları" @@ -3883,7 +3879,7 @@ msgstr "" "Oluşturulacak ön bağlantı noktalarının sayısı ({frontport_count}) seçilen " "arka port konumu sayısıyla eşleşmelidir ({rearport_count})." -#: dcim/forms/object_create.py:408 dcim/tables/devices.py:1039 +#: dcim/forms/object_create.py:408 dcim/tables/devices.py:1043 #: ipam/tables/fhrp.py:31 templates/dcim/virtualchassis.html:54 #: templates/dcim/virtualchassis_edit.html:48 templates/ipam/fhrpgroup.html:39 msgid "Members" @@ -4592,13 +4588,13 @@ msgstr "envanter kalemi rolü" msgid "inventory item roles" msgstr "envanter kalemi rolleri" -#: dcim/models/device_components.py:1230 dcim/models/devices.py:595 -#: dcim/models/devices.py:1173 dcim/models/racks.py:113 +#: dcim/models/device_components.py:1230 dcim/models/devices.py:597 +#: dcim/models/devices.py:1178 dcim/models/racks.py:113 msgid "serial number" msgstr "seri numarası" -#: dcim/models/device_components.py:1238 dcim/models/devices.py:603 -#: dcim/models/devices.py:1180 dcim/models/racks.py:120 +#: dcim/models/device_components.py:1238 dcim/models/devices.py:605 +#: dcim/models/devices.py:1185 dcim/models/racks.py:120 msgid "asset tag" msgstr "varlık etiketi" @@ -4646,7 +4642,7 @@ msgstr "üretici firma" msgid "manufacturers" msgstr "üreticiler" -#: dcim/models/devices.py:82 dcim/models/devices.py:381 +#: dcim/models/devices.py:82 dcim/models/devices.py:382 msgid "model" msgstr "model" @@ -4654,11 +4650,11 @@ msgstr "model" msgid "default platform" msgstr "varsayılan platform" -#: dcim/models/devices.py:98 dcim/models/devices.py:385 +#: dcim/models/devices.py:98 dcim/models/devices.py:386 msgid "part number" msgstr "parça numarası" -#: dcim/models/devices.py:101 dcim/models/devices.py:388 +#: dcim/models/devices.py:101 dcim/models/devices.py:389 msgid "Discrete part number (optional)" msgstr "Ayrık parça numarası (isteğe bağlı)" @@ -4694,7 +4690,7 @@ msgstr "" "Ana cihazlar, alt aygıtları cihaz yuvalarında barındırır. Bu cihaz türü " "ebeveyn veya çocuk değilse boş bırakın." -#: dcim/models/devices.py:128 dcim/models/devices.py:647 +#: dcim/models/devices.py:128 dcim/models/devices.py:649 msgid "airflow" msgstr "hava akımı" @@ -4706,11 +4702,11 @@ msgstr "cihaz tipi" msgid "device types" msgstr "cihaz türleri" -#: dcim/models/devices.py:289 +#: dcim/models/devices.py:290 msgid "U height must be in increments of 0.5 rack units." msgstr "U yüksekliği 0,5 raf ünitesi artışlarla olmalıdır." -#: dcim/models/devices.py:306 +#: dcim/models/devices.py:307 #, python-brace-format msgid "" "Device {device} in rack {rack} does not have sufficient space to accommodate" @@ -4719,7 +4715,7 @@ msgstr "" "Aygıt {device} rafta {rack} bir yüksekliği barındırmak için yeterli alana " "sahip değildir {height}U" -#: dcim/models/devices.py:321 +#: dcim/models/devices.py:322 #, python-brace-format msgid "" "Unable to set 0U height: Found {racked_instance_count} " @@ -4729,7 +4725,7 @@ msgstr "" "href=\"{url}\">{racked_instance_count} örnekler zaten raflara monte " "edilmiştir." -#: dcim/models/devices.py:330 +#: dcim/models/devices.py:331 msgid "" "Must delete all device bay templates associated with this device before " "declassifying it as a parent device." @@ -4737,164 +4733,164 @@ msgstr "" "Ana aygıt olarak sınıflandırmadan önce bu aygıtla ilişkili tüm aygıt yuvası " "şablonlarını silmeniz gerekir." -#: dcim/models/devices.py:336 +#: dcim/models/devices.py:337 msgid "Child device types must be 0U." msgstr "Çocuk cihaz türleri 0U olmalıdır." -#: dcim/models/devices.py:404 +#: dcim/models/devices.py:405 msgid "module type" msgstr "modül tipi" -#: dcim/models/devices.py:405 +#: dcim/models/devices.py:406 msgid "module types" msgstr "modül türleri" -#: dcim/models/devices.py:473 +#: dcim/models/devices.py:475 msgid "Virtual machines may be assigned to this role" msgstr "Sanal makineler bu role atanabilir" -#: dcim/models/devices.py:485 +#: dcim/models/devices.py:487 msgid "device role" msgstr "cihaz rolü" -#: dcim/models/devices.py:486 +#: dcim/models/devices.py:488 msgid "device roles" msgstr "cihaz rolleri" -#: dcim/models/devices.py:503 +#: dcim/models/devices.py:505 msgid "Optionally limit this platform to devices of a certain manufacturer" msgstr "" "İsteğe bağlı olarak bu platformu belirli bir üreticinin cihazlarıyla " "sınırlayın" -#: dcim/models/devices.py:515 +#: dcim/models/devices.py:517 msgid "platform" msgstr "platform" -#: dcim/models/devices.py:516 +#: dcim/models/devices.py:518 msgid "platforms" msgstr "platformlar" -#: dcim/models/devices.py:564 +#: dcim/models/devices.py:566 msgid "The function this device serves" msgstr "Bu cihazın hizmet ettiği işlev" -#: dcim/models/devices.py:596 +#: dcim/models/devices.py:598 msgid "Chassis serial number, assigned by the manufacturer" msgstr "Üretici tarafından atanan şasi seri numarası" -#: dcim/models/devices.py:604 dcim/models/devices.py:1181 +#: dcim/models/devices.py:606 dcim/models/devices.py:1186 msgid "A unique tag used to identify this device" msgstr "Bu cihazı tanımlamak için kullanılan benzersiz bir etiket" -#: dcim/models/devices.py:631 +#: dcim/models/devices.py:633 msgid "position (U)" msgstr "pozisyon (U)" -#: dcim/models/devices.py:638 +#: dcim/models/devices.py:640 msgid "rack face" msgstr "raf yüzü" -#: dcim/models/devices.py:658 dcim/models/devices.py:1390 +#: dcim/models/devices.py:660 dcim/models/devices.py:1395 #: virtualization/models/virtualmachines.py:98 msgid "primary IPv4" msgstr "birincil IPv4" -#: dcim/models/devices.py:666 dcim/models/devices.py:1398 +#: dcim/models/devices.py:668 dcim/models/devices.py:1403 #: virtualization/models/virtualmachines.py:106 msgid "primary IPv6" msgstr "birincil IPv6" -#: dcim/models/devices.py:674 +#: dcim/models/devices.py:676 msgid "out-of-band IP" msgstr "bant dışı IP" -#: dcim/models/devices.py:691 +#: dcim/models/devices.py:693 msgid "VC position" msgstr "VC pozisyonu" -#: dcim/models/devices.py:695 +#: dcim/models/devices.py:697 msgid "Virtual chassis position" msgstr "Sanal şasi konumu" -#: dcim/models/devices.py:698 +#: dcim/models/devices.py:700 msgid "VC priority" msgstr "VC önceliği" -#: dcim/models/devices.py:702 +#: dcim/models/devices.py:704 msgid "Virtual chassis master election priority" msgstr "Sanal şasi ana seçim önceliği" -#: dcim/models/devices.py:705 dcim/models/sites.py:207 +#: dcim/models/devices.py:707 dcim/models/sites.py:207 msgid "latitude" msgstr "enlem" -#: dcim/models/devices.py:710 dcim/models/devices.py:718 +#: dcim/models/devices.py:712 dcim/models/devices.py:720 #: dcim/models/sites.py:212 dcim/models/sites.py:220 msgid "GPS coordinate in decimal format (xx.yyyyyy)" msgstr "Ondalık formatta GPS koordinatı (xx.yyyyyy)" -#: dcim/models/devices.py:713 dcim/models/sites.py:215 +#: dcim/models/devices.py:715 dcim/models/sites.py:215 msgid "longitude" msgstr "boylam" -#: dcim/models/devices.py:786 +#: dcim/models/devices.py:788 msgid "Device name must be unique per site." msgstr "Aygıt adı site başına benzersiz olmalıdır." -#: dcim/models/devices.py:797 ipam/models/services.py:75 +#: dcim/models/devices.py:799 ipam/models/services.py:75 msgid "device" msgstr "cihaz" -#: dcim/models/devices.py:798 +#: dcim/models/devices.py:800 msgid "devices" msgstr "cihazlar" -#: dcim/models/devices.py:838 +#: dcim/models/devices.py:840 #, python-brace-format msgid "Rack {rack} does not belong to site {site}." msgstr "Raf {rack} siteye ait değil {site}." -#: dcim/models/devices.py:843 +#: dcim/models/devices.py:845 #, python-brace-format msgid "Location {location} does not belong to site {site}." msgstr "{location} Konum {site} adlı siteye ait değil." -#: dcim/models/devices.py:849 +#: dcim/models/devices.py:851 #, python-brace-format msgid "Rack {rack} does not belong to location {location}." msgstr "{rack} rafı {location} adlı konuma ait değil." -#: dcim/models/devices.py:856 +#: dcim/models/devices.py:858 msgid "Cannot select a rack face without assigning a rack." msgstr "Bir raf atamadan raf yüzü seçilemez." -#: dcim/models/devices.py:860 +#: dcim/models/devices.py:862 msgid "Cannot select a rack position without assigning a rack." msgstr "Bir raf atamadan raf konumu seçilemez." -#: dcim/models/devices.py:866 +#: dcim/models/devices.py:868 msgid "Position must be in increments of 0.5 rack units." msgstr "Konum 0,5 raf ünitesinin artışlarında olmalıdır." -#: dcim/models/devices.py:870 +#: dcim/models/devices.py:872 msgid "Must specify rack face when defining rack position." msgstr "Raf konumunu tanımlarken raf yüzü belirtilmelidir." -#: dcim/models/devices.py:878 +#: dcim/models/devices.py:880 #, python-brace-format msgid "" "A 0U device type ({device_type}) cannot be assigned to a rack position." msgstr "Bir 0U cihaz tipi ({device_type}) bir raf konumuna atanamaz." -#: dcim/models/devices.py:889 +#: dcim/models/devices.py:891 msgid "" "Child device types cannot be assigned to a rack face. This is an attribute " "of the parent device." msgstr "" "Alt aygıt türleri bir raf yüzüne atanamaz. Bu, ana cihazın bir özelliğidir." -#: dcim/models/devices.py:896 +#: dcim/models/devices.py:898 msgid "" "Child device types cannot be assigned to a rack position. This is an " "attribute of the parent device." @@ -4902,7 +4898,7 @@ msgstr "" "Alt aygıt türleri bir raf konumuna atanamaz. Bu, ana aygıtın bir " "özelliğidir." -#: dcim/models/devices.py:910 +#: dcim/models/devices.py:912 #, python-brace-format msgid "" "U{position} is already occupied or does not have sufficient space to " @@ -4911,22 +4907,22 @@ msgstr "" "U{position} zaten işgal edilmiş veya bu cihaz tipini barındırmak için " "yeterli alana sahip değil: {device_type} ({u_height}U)" -#: dcim/models/devices.py:925 +#: dcim/models/devices.py:927 #, python-brace-format msgid "{ip} is not an IPv4 address." msgstr "{ip} Bu bir IPv4 adresi değildir." -#: dcim/models/devices.py:934 dcim/models/devices.py:949 +#: dcim/models/devices.py:936 dcim/models/devices.py:951 #, python-brace-format msgid "The specified IP address ({ip}) is not assigned to this device." msgstr "Belirtilen IP adresi ({ip}) bu cihaza atanmamıştır." -#: dcim/models/devices.py:940 +#: dcim/models/devices.py:942 #, python-brace-format msgid "{ip} is not an IPv6 address." msgstr "{ip} Bu bir IPv6 adresi değildir." -#: dcim/models/devices.py:967 +#: dcim/models/devices.py:969 #, python-brace-format msgid "" "The assigned platform is limited to {platform_manufacturer} device types, " @@ -4935,45 +4931,45 @@ msgstr "" "Atanan platform aşağıdakilerle sınırlıdır {platform_manufacturer} cihaz " "türleri, ancak bu cihazın türü şunlara aittir {devicetype_manufacturer}." -#: dcim/models/devices.py:978 +#: dcim/models/devices.py:980 #, python-brace-format msgid "The assigned cluster belongs to a different site ({site})" msgstr "Atanan küme farklı bir siteye aittir ({site})" -#: dcim/models/devices.py:986 +#: dcim/models/devices.py:988 msgid "A device assigned to a virtual chassis must have its position defined." msgstr "Sanal bir kasaya atanan bir aygıtın konumu tanımlanmış olmalıdır." -#: dcim/models/devices.py:1188 +#: dcim/models/devices.py:1193 msgid "module" msgstr "modül" -#: dcim/models/devices.py:1189 +#: dcim/models/devices.py:1194 msgid "modules" msgstr "modülleri" -#: dcim/models/devices.py:1205 +#: dcim/models/devices.py:1210 #, python-brace-format msgid "" "Module must be installed within a module bay belonging to the assigned " "device ({device})." msgstr "Modül, atanan cihaza ait bir modül bölmesine kurulmalıdır ({device})." -#: dcim/models/devices.py:1309 +#: dcim/models/devices.py:1314 msgid "domain" msgstr "domain" -#: dcim/models/devices.py:1322 dcim/models/devices.py:1323 +#: dcim/models/devices.py:1327 dcim/models/devices.py:1328 msgid "virtual chassis" msgstr "sanal kasa" -#: dcim/models/devices.py:1338 +#: dcim/models/devices.py:1343 #, python-brace-format msgid "" "The selected master ({master}) is not assigned to this virtual chassis." msgstr "Seçilen usta ({master}) bu sanal kasaya atanmamıştır." -#: dcim/models/devices.py:1354 +#: dcim/models/devices.py:1359 #, python-brace-format msgid "" "Unable to delete virtual chassis {self}. There are member interfaces which " @@ -4982,33 +4978,33 @@ msgstr "" "Sanal kasa silinemiyor {self}. Çapraz şasi LAG arabirimleri oluşturan üye " "arayüzleri vardır." -#: dcim/models/devices.py:1379 vpn/models/l2vpn.py:37 +#: dcim/models/devices.py:1384 vpn/models/l2vpn.py:37 msgid "identifier" msgstr "belirlemek" -#: dcim/models/devices.py:1380 +#: dcim/models/devices.py:1385 msgid "Numeric identifier unique to the parent device" msgstr "Ana aygıta benzersiz sayısal tanımlayıcı" -#: dcim/models/devices.py:1408 extras/models/models.py:129 +#: dcim/models/devices.py:1413 extras/models/models.py:129 #: extras/models/models.py:724 netbox/models/__init__.py:114 msgid "comments" msgstr "yorumlar" -#: dcim/models/devices.py:1424 +#: dcim/models/devices.py:1429 msgid "virtual device context" msgstr "sanal cihaz bağlamı" -#: dcim/models/devices.py:1425 +#: dcim/models/devices.py:1430 msgid "virtual device contexts" msgstr "sanal cihaz bağlamları" -#: dcim/models/devices.py:1457 +#: dcim/models/devices.py:1462 #, python-brace-format msgid "{ip} is not an IPv{family} address." msgstr "{ip} IPV değil{family} adres." -#: dcim/models/devices.py:1463 +#: dcim/models/devices.py:1468 msgid "Primary IP address must belong to an interface on the assigned device." msgstr "Birincil IP adresi, atanan cihazdaki bir arayüze ait olmalıdır." @@ -5390,7 +5386,7 @@ msgstr "Konsol Bağlantı Noktası" msgid "Reachable" msgstr "Ulaşılabilir" -#: dcim/tables/connections.py:46 dcim/tables/devices.py:529 +#: dcim/tables/connections.py:46 dcim/tables/devices.py:533 #: templates/dcim/inventoryitem_edit.html:64 #: templates/dcim/poweroutlet.html:47 templates/dcim/powerport.html:18 msgid "Power Port" @@ -5409,7 +5405,7 @@ msgstr "Aygıtlar" msgid "VMs" msgstr "Sanal Makineler" -#: dcim/tables/devices.py:133 dcim/tables/devices.py:245 +#: dcim/tables/devices.py:133 dcim/tables/devices.py:249 #: extras/forms/model_forms.py:515 templates/dcim/device.html:114 #: templates/dcim/device/render_config.html:11 #: templates/dcim/device/render_config.html:15 @@ -5418,62 +5414,62 @@ msgstr "Sanal Makineler" #: templates/virtualization/virtualmachine.html:47 #: templates/virtualization/virtualmachine/render_config.html:11 #: templates/virtualization/virtualmachine/render_config.html:15 -#: virtualization/tables/virtualmachines.py:93 +#: virtualization/tables/virtualmachines.py:106 msgid "Config Template" msgstr "Yapılandırma Şablonu" -#: dcim/tables/devices.py:216 dcim/tables/devices.py:1074 +#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 #: ipam/forms/bulk_import.py:511 ipam/forms/model_forms.py:296 #: ipam/tables/ip.py:352 ipam/tables/ip.py:418 ipam/tables/ip.py:441 #: templates/ipam/ipaddress.html:12 templates/ipam/ipaddress_edit.html:14 -#: virtualization/tables/virtualmachines.py:81 +#: virtualization/tables/virtualmachines.py:94 msgid "IP Address" msgstr "IP Adresi" -#: dcim/tables/devices.py:220 dcim/tables/devices.py:1078 -#: virtualization/tables/virtualmachines.py:72 +#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 +#: virtualization/tables/virtualmachines.py:85 msgid "IPv4 Address" msgstr "IPv4 Adresi" -#: dcim/tables/devices.py:224 dcim/tables/devices.py:1082 -#: virtualization/tables/virtualmachines.py:76 +#: dcim/tables/devices.py:228 dcim/tables/devices.py:1086 +#: virtualization/tables/virtualmachines.py:89 msgid "IPv6 Address" msgstr "IPv6 Adresi" -#: dcim/tables/devices.py:239 +#: dcim/tables/devices.py:243 msgid "VC Position" msgstr "VC Pozisyonu" -#: dcim/tables/devices.py:242 +#: dcim/tables/devices.py:246 msgid "VC Priority" msgstr "VC Önceliği" -#: dcim/tables/devices.py:249 templates/dcim/device_edit.html:38 +#: dcim/tables/devices.py:253 templates/dcim/device_edit.html:38 #: templates/dcim/devicebay_populate.html:16 msgid "Parent Device" msgstr "Ebeveyn Aygıtı" -#: dcim/tables/devices.py:254 +#: dcim/tables/devices.py:258 msgid "Position (Device Bay)" msgstr "Konum (Aygıt Yuvası)" -#: dcim/tables/devices.py:263 +#: dcim/tables/devices.py:267 msgid "Console ports" msgstr "Konsol bağlantı noktaları" -#: dcim/tables/devices.py:266 +#: dcim/tables/devices.py:270 msgid "Console server ports" msgstr "Konsol sunucusu bağlantı noktaları" -#: dcim/tables/devices.py:269 +#: dcim/tables/devices.py:273 msgid "Power ports" msgstr "Güç bağlantı noktaları" -#: dcim/tables/devices.py:272 +#: dcim/tables/devices.py:276 msgid "Power outlets" msgstr "Elektrik prizleri" -#: dcim/tables/devices.py:275 dcim/tables/devices.py:1087 +#: dcim/tables/devices.py:279 dcim/tables/devices.py:1091 #: dcim/tables/devicetypes.py:125 dcim/views.py:1005 dcim/views.py:1244 #: dcim/views.py:1930 netbox/navigation/menu.py:82 #: netbox/navigation/menu.py:238 templates/dcim/device/base.html:37 @@ -5483,53 +5479,53 @@ msgstr "Elektrik prizleri" #: templates/dcim/virtualdevicecontext.html:85 #: templates/virtualization/virtualmachine/base.html:27 #: templates/virtualization/virtualmachine_list.html:14 -#: virtualization/tables/virtualmachines.py:87 virtualization/views.py:368 +#: virtualization/tables/virtualmachines.py:100 virtualization/views.py:368 #: wireless/tables/wirelesslan.py:55 msgid "Interfaces" msgstr "Arayüzler" -#: dcim/tables/devices.py:278 +#: dcim/tables/devices.py:282 msgid "Front ports" msgstr "Ön bağlantı noktaları" -#: dcim/tables/devices.py:284 +#: dcim/tables/devices.py:288 msgid "Device bays" msgstr "Aygıt yuvaları" -#: dcim/tables/devices.py:287 +#: dcim/tables/devices.py:291 msgid "Module bays" msgstr "Modül bölmeleri" -#: dcim/tables/devices.py:290 +#: dcim/tables/devices.py:294 msgid "Inventory items" msgstr "Envanter kalemleri" -#: dcim/tables/devices.py:329 dcim/tables/modules.py:56 +#: dcim/tables/devices.py:333 dcim/tables/modules.py:56 #: templates/dcim/modulebay.html:17 msgid "Module Bay" msgstr "Modül Yuvası" -#: dcim/tables/devices.py:350 +#: dcim/tables/devices.py:354 msgid "Cable Color" msgstr "Kablo Rengi" -#: dcim/tables/devices.py:356 +#: dcim/tables/devices.py:360 msgid "Link Peers" msgstr "Meslektaşları Bağla" -#: dcim/tables/devices.py:359 +#: dcim/tables/devices.py:363 msgid "Mark Connected" msgstr "Bağlı İşaretle" -#: dcim/tables/devices.py:475 +#: dcim/tables/devices.py:479 msgid "Maximum draw (W)" msgstr "Maksimum çekim (W)" -#: dcim/tables/devices.py:478 +#: dcim/tables/devices.py:482 msgid "Allocated draw (W)" msgstr "Tahsis edilen çekiliş (W)" -#: dcim/tables/devices.py:578 ipam/forms/model_forms.py:707 +#: dcim/tables/devices.py:582 ipam/forms/model_forms.py:711 #: ipam/tables/fhrp.py:28 ipam/views.py:597 ipam/views.py:691 #: netbox/navigation/menu.py:146 netbox/navigation/menu.py:148 #: templates/dcim/interface.html:351 templates/ipam/ipaddress_bulk_add.html:15 @@ -5538,12 +5534,12 @@ msgstr "Tahsis edilen çekiliş (W)" msgid "IP Addresses" msgstr "IP Adresleri" -#: dcim/tables/devices.py:584 netbox/navigation/menu.py:190 +#: dcim/tables/devices.py:588 netbox/navigation/menu.py:190 #: templates/ipam/inc/panels/fhrp_groups.html:5 msgid "FHRP Groups" msgstr "FHRP Grupları" -#: dcim/tables/devices.py:596 templates/dcim/interface.html:90 +#: dcim/tables/devices.py:600 templates/dcim/interface.html:90 #: templates/virtualization/vminterface.html:70 templates/vpn/tunnel.html:18 #: templates/vpn/tunneltermination.html:14 vpn/forms/bulk_edit.py:75 #: vpn/forms/bulk_import.py:76 vpn/forms/filtersets.py:41 @@ -5552,20 +5548,20 @@ msgstr "FHRP Grupları" msgid "Tunnel" msgstr "Tünel" -#: dcim/tables/devices.py:621 dcim/tables/devicetypes.py:224 +#: dcim/tables/devices.py:625 dcim/tables/devicetypes.py:224 #: templates/dcim/interface.html:66 msgid "Management Only" msgstr "Yalnızca Yönetim" -#: dcim/tables/devices.py:629 +#: dcim/tables/devices.py:633 msgid "Wireless link" msgstr "Kablosuz bağlantı" -#: dcim/tables/devices.py:639 +#: dcim/tables/devices.py:643 msgid "VDCs" msgstr "VDC'ler" -#: dcim/tables/devices.py:647 dcim/tables/devicetypes.py:48 +#: dcim/tables/devices.py:651 dcim/tables/devicetypes.py:48 #: dcim/tables/devicetypes.py:140 dcim/views.py:1080 dcim/views.py:2023 #: netbox/navigation/menu.py:91 templates/dcim/device/base.html:52 #: templates/dcim/device_list.html:71 templates/dcim/devicetype/base.html:49 @@ -5574,7 +5570,7 @@ msgstr "VDC'ler" msgid "Inventory Items" msgstr "Envanter Öğeleri" -#: dcim/tables/devices.py:728 +#: dcim/tables/devices.py:732 #: templates/circuits/inc/circuit_termination.html:80 #: templates/dcim/consoleport.html:81 templates/dcim/consoleserverport.html:81 #: templates/dcim/frontport.html:53 templates/dcim/frontport.html:125 @@ -5583,28 +5579,28 @@ msgstr "Envanter Öğeleri" msgid "Rear Port" msgstr "Arka Bağlantı Noktası" -#: dcim/tables/devices.py:893 templates/dcim/modulebay.html:51 +#: dcim/tables/devices.py:897 templates/dcim/modulebay.html:51 msgid "Installed Module" msgstr "Yüklü Modül" -#: dcim/tables/devices.py:896 +#: dcim/tables/devices.py:900 msgid "Module Serial" msgstr "Modül Seri" -#: dcim/tables/devices.py:900 +#: dcim/tables/devices.py:904 msgid "Module Asset Tag" msgstr "Modül Varlık Etiketi" -#: dcim/tables/devices.py:909 +#: dcim/tables/devices.py:913 msgid "Module Status" msgstr "Modül Durumu" -#: dcim/tables/devices.py:951 dcim/tables/devicetypes.py:308 +#: dcim/tables/devices.py:955 dcim/tables/devicetypes.py:308 #: templates/dcim/inventoryitem.html:41 msgid "Component" msgstr "Bileşen" -#: dcim/tables/devices.py:1006 +#: dcim/tables/devices.py:1010 msgid "Items" msgstr "Öğeler" @@ -6167,7 +6163,7 @@ msgid "Cluster type (slug)" msgstr "Küme tipi (kısa ad)" #: extras/filtersets.py:490 ipam/forms/bulk_edit.py:475 -#: ipam/forms/model_forms.py:585 virtualization/forms/filtersets.py:108 +#: ipam/forms/model_forms.py:589 virtualization/forms/filtersets.py:108 msgid "Cluster group" msgstr "Küme grubu" @@ -6298,8 +6294,8 @@ msgid "Is active" msgstr "Aktif" #: extras/forms/bulk_import.py:34 extras/forms/bulk_import.py:115 -#: extras/forms/bulk_import.py:130 extras/forms/bulk_import.py:153 -#: extras/forms/bulk_import.py:177 extras/forms/filtersets.py:114 +#: extras/forms/bulk_import.py:136 extras/forms/bulk_import.py:159 +#: extras/forms/bulk_import.py:183 extras/forms/filtersets.py:114 #: extras/forms/filtersets.py:160 extras/forms/filtersets.py:201 #: extras/forms/model_forms.py:43 extras/forms/model_forms.py:127 #: extras/forms/model_forms.py:156 extras/forms/model_forms.py:197 @@ -6308,8 +6304,8 @@ msgid "Content types" msgstr "İçerik türleri" #: extras/forms/bulk_import.py:36 extras/forms/bulk_import.py:117 -#: extras/forms/bulk_import.py:132 extras/forms/bulk_import.py:155 -#: extras/forms/bulk_import.py:179 tenancy/forms/bulk_import.py:96 +#: extras/forms/bulk_import.py:138 extras/forms/bulk_import.py:161 +#: extras/forms/bulk_import.py:185 tenancy/forms/bulk_import.py:96 msgid "One or more assigned object types" msgstr "Bir veya daha fazla atanmış nesne türü" @@ -6356,29 +6352,39 @@ msgstr "" "seçeneklerinin alıntılanmış dizesi: “Seçim1:First Choice, Choice2:Second " "Choice”" -#: extras/forms/bulk_import.py:182 +#: extras/forms/bulk_import.py:120 extras/models/models.py:353 +msgid "button class" +msgstr "düğme sınıfı" + +#: extras/forms/bulk_import.py:123 extras/models/models.py:357 +msgid "" +"The class of the first link in a group will be used for the dropdown button" +msgstr "" +"Bir gruptaki ilk bağlantının sınıfı açılır düğme için kullanılacaktır." + +#: extras/forms/bulk_import.py:188 msgid "Action object" msgstr "Eylem nesnesi" -#: extras/forms/bulk_import.py:184 +#: extras/forms/bulk_import.py:190 msgid "Webhook name or script as dotted path module.Class" msgstr "Noktalı yol olarak Webhook adı veya komut dosyası module.Class" -#: extras/forms/bulk_import.py:205 +#: extras/forms/bulk_import.py:211 #, python-brace-format msgid "Webhook {name} not found" msgstr "Web kancası {name} bulunamadı" -#: extras/forms/bulk_import.py:214 +#: extras/forms/bulk_import.py:220 #, python-brace-format msgid "Script {name} not found" msgstr "Senaryo {name} bulunamadı" -#: extras/forms/bulk_import.py:236 +#: extras/forms/bulk_import.py:242 msgid "Assigned object type" msgstr "Atanan nesne türü" -#: extras/forms/bulk_import.py:241 +#: extras/forms/bulk_import.py:247 msgid "The classification of entry" msgstr "Girişin sınıflandırılması" @@ -7325,16 +7331,6 @@ msgstr "Bağlantı URL'si için Jinja2 şablon kodu" msgid "Links with the same group will appear as a dropdown menu" msgstr "Aynı gruba sahip bağlantılar açılır menü olarak görünecektir" -#: extras/models/models.py:353 -msgid "button class" -msgstr "düğme sınıfı" - -#: extras/models/models.py:357 -msgid "" -"The class of the first link in a group will be used for the dropdown button" -msgstr "" -"Bir gruptaki ilk bağlantının sınıfı açılır düğme için kullanılacaktır." - #: extras/models/models.py:360 msgid "new window" msgstr "yeni pencere" @@ -7522,7 +7518,7 @@ msgid "staged changes" msgstr "aşamalı değişiklikler" #: extras/models/tags.py:40 -msgid "The object type(s) to which this this tag can be applied." +msgid "The object type(s) to which this tag can be applied." msgstr "Bu etiketin uygulanabileceği nesne türü (ler) dir." #: extras/models/tags.py:49 @@ -7821,7 +7817,7 @@ msgid "VLAN number (1-4094)" msgstr "VLAN numarası (1-4094)" #: ipam/filtersets.py:437 ipam/filtersets.py:441 ipam/filtersets.py:533 -#: ipam/forms/model_forms.py:444 templates/tenancy/contact.html:54 +#: ipam/forms/model_forms.py:430 templates/tenancy/contact.html:54 #: tenancy/forms/bulk_edit.py:112 msgid "Address" msgstr "Adres" @@ -7990,7 +7986,7 @@ msgid "Authentication key" msgstr "Kimlik doğrulama anahtarı" #: ipam/forms/bulk_edit.py:404 ipam/forms/filtersets.py:369 -#: ipam/forms/model_forms.py:455 netbox/navigation/menu.py:376 +#: ipam/forms/model_forms.py:441 netbox/navigation/menu.py:376 #: templates/ipam/fhrpgroup.html:51 #: templates/wireless/inc/authentication_attrs.html:5 #: wireless/forms/bulk_edit.py:90 wireless/forms/bulk_edit.py:137 @@ -8007,11 +8003,11 @@ msgstr "Minimum çocuk VLAN VID" msgid "Maximum child VLAN VID" msgstr "Maksimum çocuk VLAN VID" -#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:527 +#: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:531 msgid "Scope type" msgstr "Kapsam türü" -#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:600 +#: ipam/forms/bulk_edit.py:489 ipam/forms/model_forms.py:604 #: ipam/tables/vlans.py:71 templates/ipam/vlangroup.html:39 msgid "Scope" msgstr "Kapsam" @@ -8020,8 +8016,8 @@ msgstr "Kapsam" msgid "Site & Group" msgstr "Site ve Grup" -#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:663 -#: ipam/forms/model_forms.py:697 ipam/tables/services.py:19 +#: ipam/forms/bulk_edit.py:574 ipam/forms/model_forms.py:667 +#: ipam/forms/model_forms.py:701 ipam/tables/services.py:19 #: ipam/tables/services.py:49 templates/ipam/service.html:39 #: templates/ipam/servicetemplate.html:24 msgid "Ports" @@ -8061,7 +8057,7 @@ msgid "Parent device of assigned interface (if any)" msgstr "Atanan arayüzün ana cihazı (varsa)" #: ipam/forms/bulk_import.py:310 ipam/forms/bulk_import.py:496 -#: ipam/forms/model_forms.py:691 virtualization/filtersets.py:284 +#: ipam/forms/model_forms.py:695 virtualization/filtersets.py:284 #: virtualization/filtersets.py:323 virtualization/forms/bulk_edit.py:199 #: virtualization/forms/bulk_edit.py:325 #: virtualization/forms/bulk_import.py:146 @@ -8237,8 +8233,8 @@ msgstr "Liman" #: virtualization/forms/filtersets.py:189 #: virtualization/forms/filtersets.py:234 #: virtualization/forms/model_forms.py:223 -#: virtualization/tables/virtualmachines.py:115 -#: virtualization/tables/virtualmachines.py:168 vpn/choices.py:45 +#: virtualization/tables/virtualmachines.py:128 +#: virtualization/tables/virtualmachines.py:181 vpn/choices.py:45 #: vpn/forms/filtersets.py:289 vpn/forms/model_forms.py:161 #: vpn/forms/model_forms.py:172 vpn/forms/model_forms.py:274 msgid "Virtual Machine" @@ -8261,7 +8257,7 @@ msgstr "Site/VLAN Ataması" msgid "IP Range" msgstr "IP Aralığı" -#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:454 +#: ipam/forms/model_forms.py:285 ipam/forms/model_forms.py:440 #: templates/ipam/fhrpgroup.html:19 templates/ipam/ipaddress_edit.html:52 msgid "FHRP Group" msgstr "FHRP Grubu" @@ -8274,7 +8270,7 @@ msgstr "Bunu cihaz/VM için birincil IP yapın" msgid "An IP address can only be assigned to a single object." msgstr "IP adresi yalnızca tek bir nesneye atanabilir." -#: ipam/forms/model_forms.py:357 ipam/models/ip.py:877 +#: ipam/forms/model_forms.py:357 ipam/models/ip.py:896 msgid "" "Cannot reassign IP address while it is designated as the primary IP for the " "parent object" @@ -8287,32 +8283,25 @@ msgid "" msgstr "" "Yalnızca bir arayüze atanan IP adresleri birincil IP olarak belirlenebilir." -#: ipam/forms/model_forms.py:373 -#, python-brace-format -msgid "{ip} is a network ID, which may not be assigned to an interface." -msgstr "{ip} bir arayüze atanamayacak bir ağ kimliğidir." - -#: ipam/forms/model_forms.py:379 -#, python-brace-format -msgid "" -"{ip} is a broadcast address, which may not be assigned to an interface." -msgstr "{ip} bir arayüze atanamayacak bir yayın adresidir." - -#: ipam/forms/model_forms.py:456 +#: ipam/forms/model_forms.py:442 msgid "Virtual IP Address" msgstr "Sanal IP Adresi" -#: ipam/forms/model_forms.py:598 ipam/forms/model_forms.py:637 +#: ipam/forms/model_forms.py:523 +msgid "Assignment already exists" +msgstr "Atama zaten var" + +#: ipam/forms/model_forms.py:602 ipam/forms/model_forms.py:641 #: ipam/tables/ip.py:250 templates/ipam/vlan_edit.html:37 #: templates/ipam/vlangroup.html:27 msgid "VLAN Group" msgstr "VLAN Grubu" -#: ipam/forms/model_forms.py:599 +#: ipam/forms/model_forms.py:603 msgid "Child VLANs" msgstr "Çocuk VLAN'ları" -#: ipam/forms/model_forms.py:668 ipam/forms/model_forms.py:702 +#: ipam/forms/model_forms.py:672 ipam/forms/model_forms.py:706 msgid "" "Comma-separated list of one or more port numbers. A range may be specified " "using a hyphen." @@ -8320,15 +8309,15 @@ msgstr "" "Bir veya daha fazla bağlantı noktası numarasının virgülle ayrılmış listesi. " "Bir aralık bir tire kullanılarak belirtilebilir." -#: ipam/forms/model_forms.py:673 templates/ipam/servicetemplate.html:12 +#: ipam/forms/model_forms.py:677 templates/ipam/servicetemplate.html:12 msgid "Service Template" msgstr "Hizmet Şablonu" -#: ipam/forms/model_forms.py:724 +#: ipam/forms/model_forms.py:728 msgid "Service template" msgstr "Hizmet şablonu" -#: ipam/forms/model_forms.py:754 +#: ipam/forms/model_forms.py:758 msgid "" "Must specify name, protocol, and port(s) if not using a service template." msgstr "" @@ -8494,12 +8483,12 @@ msgstr "önekleri" msgid "Cannot create prefix with /0 mask." msgstr "/0 maskesi ile önek oluşturulamıyor." -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 #, python-brace-format msgid "VRF {vrf}" msgstr "VRF {vrf}" -#: ipam/models/ip.py:323 ipam/models/ip.py:854 +#: ipam/models/ip.py:323 ipam/models/ip.py:873 msgid "global table" msgstr "küresel tablo" @@ -8594,12 +8583,23 @@ msgstr "IP adresleri" msgid "Cannot create IP address with /0 mask." msgstr "/0 maskesi ile IP adresi oluşturulamıyor." -#: ipam/models/ip.py:856 +#: ipam/models/ip.py:850 +#, python-brace-format +msgid "{ip} is a network ID, which may not be assigned to an interface." +msgstr "{ip} bir arayüze atanamayacak bir ağ kimliğidir." + +#: ipam/models/ip.py:861 +#, python-brace-format +msgid "" +"{ip} is a broadcast address, which may not be assigned to an interface." +msgstr "{ip} bir arayüze atanamayacak bir yayın adresidir." + +#: ipam/models/ip.py:875 #, python-brace-format msgid "Duplicate IP address found in {table}: {ipaddress}" msgstr "Yinelenen IP adresi şurada bulundu {table}: {ipaddress}" -#: ipam/models/ip.py:883 +#: ipam/models/ip.py:902 msgid "Only IPv6 addresses can be assigned SLAAC status" msgstr "Yalnızca IPv6 adreslerine SLAAC durumu atanabilir" @@ -9375,7 +9375,7 @@ msgstr "Sanal Makineler" #: templates/virtualization/virtualmachine.html:177 #: templates/virtualization/virtualmachine/base.html:32 #: templates/virtualization/virtualmachine_list.html:21 -#: virtualization/tables/virtualmachines.py:90 virtualization/views.py:389 +#: virtualization/tables/virtualmachines.py:103 virtualization/views.py:389 msgid "Virtual Disks" msgstr "Sanal Diskler" @@ -10299,8 +10299,8 @@ msgstr "Çizelgeleme" #: templates/core/job.html:66 #, python-format -msgid "every %(interval)s seconds" -msgstr "her bir %(interval)s saniyeler" +msgid "every %(interval)s minutes" +msgstr "her bir %(interval)s dakikalar" #: templates/dcim/bulk_disconnect.html:9 #, python-format @@ -12852,66 +12852,66 @@ msgstr "Bu nesne türü için kısıtlamalar desteklenmez." msgid "Invalid filter for {model}: {error}" msgstr "Geçersiz filtre {model}: {error}" -#: users/models.py:54 +#: users/models.py:55 msgid "user" msgstr "kullanıcı" -#: users/models.py:55 +#: users/models.py:56 msgid "users" msgstr "kullanıcıları" -#: users/models.py:66 +#: users/models.py:67 msgid "A user with this username already exists." msgstr "Bu kullanıcı adına sahip bir kullanıcı zaten var." -#: users/models.py:78 vpn/models/crypto.py:42 +#: users/models.py:79 vpn/models/crypto.py:42 msgid "group" msgstr "grup" -#: users/models.py:79 +#: users/models.py:80 msgid "groups" msgstr "gruplar" -#: users/models.py:106 users/models.py:107 +#: users/models.py:107 users/models.py:108 msgid "user preferences" msgstr "kullanıcı tercihleri" -#: users/models.py:174 +#: users/models.py:175 #, python-brace-format msgid "Key '{path}' is a leaf node; cannot assign new keys" msgstr "Anahtar '{path}'bir yaprak düğümüdür; yeni anahtarlar atanamıyor" -#: users/models.py:186 +#: users/models.py:187 #, python-brace-format msgid "Key '{path}' is a dictionary; cannot assign a non-dictionary value" msgstr "Anahtar '{path}'bir sözlüktür; sözlük dışı bir değer atayamaz" -#: users/models.py:252 +#: users/models.py:253 msgid "expires" msgstr "süresi dolmak" -#: users/models.py:257 +#: users/models.py:258 msgid "last used" msgstr "son kullanılan" -#: users/models.py:262 +#: users/models.py:263 msgid "key" msgstr "anahtar" -#: users/models.py:268 +#: users/models.py:269 msgid "write enabled" msgstr "yazma etkin" -#: users/models.py:270 +#: users/models.py:271 msgid "Permit create/update/delete operations using this key" msgstr "" "Bu anahtarı kullanarak oluşturma/güncelleme/silme işlemlerine izin verin" -#: users/models.py:281 +#: users/models.py:282 msgid "allowed IPs" msgstr "izin verilen IP'ler" -#: users/models.py:283 +#: users/models.py:284 msgid "" "Allowed IPv4/IPv6 networks from where the token can be used. Leave blank for" " no restrictions. Ex: \"10.1.1.0/24, 192.168.10.16/32, 2001:DB8:1::/64\"" @@ -12920,32 +12920,32 @@ msgstr "" "olmadan boş bırakın. Örn: “10.1.1.0/24, 192.168.10.16/32, 2001: DB 8:1: " ":/64\"" -#: users/models.py:291 +#: users/models.py:296 msgid "token" msgstr "jeton" -#: users/models.py:292 +#: users/models.py:297 msgid "tokens" msgstr "jetonlar" -#: users/models.py:373 +#: users/models.py:378 msgid "The list of actions granted by this permission" msgstr "Bu izin tarafından verilen eylemlerin listesi" -#: users/models.py:378 +#: users/models.py:383 msgid "constraints" msgstr "kısıtlamaları" -#: users/models.py:379 +#: users/models.py:384 msgid "" "Queryset filter matching the applicable objects of the selected type(s)" msgstr "Seçili türlerin uygulanabilir nesneleriyle eşleşen Queryset filtresi" -#: users/models.py:386 +#: users/models.py:391 msgid "permission" msgstr "izin" -#: users/models.py:387 +#: users/models.py:392 msgid "permissions" msgstr "izinler" @@ -13215,44 +13215,53 @@ msgstr "" "Bu nesne, form oluşturulduğundan beri değiştirildi. Ayrıntılar için lütfen " "nesnenin değişiklik günlüğüne bakın." -#: utilities/forms/utils.py:42 utilities/forms/utils.py:65 -#: utilities/forms/utils.py:77 utilities/forms/utils.py:80 +#: utilities/forms/utils.py:42 utilities/forms/utils.py:68 +#: utilities/forms/utils.py:85 utilities/forms/utils.py:87 #, python-brace-format msgid "Range \"{value}\" is invalid." msgstr "Menzil”{value}“geçersiz." -#: utilities/forms/utils.py:225 +#: utilities/forms/utils.py:74 +#, python-brace-format +msgid "" +"Invalid range: Ending value ({end}) must be greater than beginning value " +"({begin})." +msgstr "" +"Geçersiz aralık: Bitiş değeri ({end}) başlangıç değerinden büyük olmalıdır " +"({begin})." + +#: utilities/forms/utils.py:232 #, python-brace-format msgid "Duplicate or conflicting column header for \"{field}\"" msgstr "Yinelenen veya çakışan sütun başlığı”{field}“" -#: utilities/forms/utils.py:231 +#: utilities/forms/utils.py:238 #, python-brace-format msgid "Duplicate or conflicting column header for \"{header}\"" msgstr "Yinelenen veya çakışan sütun başlığı”{header}“" -#: utilities/forms/utils.py:240 +#: utilities/forms/utils.py:247 #, python-brace-format msgid "Row {row}: Expected {count_expected} columns but found {count_found}" msgstr "" "Satır {row}: Bekleniyor {count_expected} sütunlar ama bulundu {count_found}" -#: utilities/forms/utils.py:263 +#: utilities/forms/utils.py:270 #, python-brace-format msgid "Unexpected column header \"{field}\" found." msgstr "Beklenmeyen sütun başlığı”{field}“bulundu." -#: utilities/forms/utils.py:265 +#: utilities/forms/utils.py:272 #, python-brace-format msgid "Column \"{field}\" is not a related object; cannot use dots" msgstr "Sütun”{field}“ilgili bir nesne değildir; nokta kullanamaz" -#: utilities/forms/utils.py:269 +#: utilities/forms/utils.py:276 #, python-brace-format msgid "Invalid related object attribute for column \"{field}\": {to_field}" msgstr "Sütun için geçersiz ilgili nesne özniteliği”{field}“: {to_field}" -#: utilities/forms/utils.py:277 +#: utilities/forms/utils.py:284 #, python-brace-format msgid "Required column header \"{header}\" not found." msgstr "Gerekli sütun başlığı”{header}“Bulunamadı." @@ -13882,6 +13891,11 @@ msgstr "Teklif" msgid "Assigned Object Type" msgstr "Atanan Nesne Türü" +#: vpn/forms/model_forms.py:94 vpn/forms/model_forms.py:129 +#: vpn/forms/model_forms.py:241 vpn/tables/tunnels.py:91 +msgid "Tunnel interface" +msgstr "Tünel arayüzü" + #: vpn/forms/model_forms.py:147 msgid "First Termination" msgstr "İlk Fesih" diff --git a/requirements.txt b/requirements.txt index 39922c9b5..2fbc25b21 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,34 +2,34 @@ bleach==6.1.0 Django==4.2.11 django-cors-headers==4.3.1 django-debug-toolbar==4.3.0 -django-filter==24.1 +django-filter==24.2 django-graphiql-debug-toolbar==0.2.0 django-mptt==0.14.0 django-pglocks==1.0.4 django-prometheus==2.3.1 django-redis==5.4.0 django-rich==1.8.0 -django-rq==2.10.1 +django-rq==2.10.2 django-taggit==5.0.1 django-tables2==2.7.0 django-timezone-field==6.1.0 djangorestframework==3.14.0 -drf-spectacular==0.27.1 -drf-spectacular-sidecar==2024.3.4 +drf-spectacular==0.27.2 +drf-spectacular-sidecar==2024.4.1 feedparser==6.0.11 graphene-django==3.0.0 gunicorn==21.2.0 Jinja2==3.1.3 -Markdown==3.5.2 -mkdocs-material==9.5.13 -mkdocstrings[python-legacy]==0.24.1 +Markdown==3.6 +mkdocs-material==9.5.17 +mkdocstrings[python-legacy]==0.24.2 netaddr==1.2.1 -Pillow==10.2.0 +Pillow==10.3.0 psycopg[binary,pool]==3.1.18 PyYAML==6.0.1 requests==2.31.0 social-auth-app-django==5.4.0 social-auth-core[openidconnect]==4.5.3 svgwrite==1.4.3 -tablib==3.5.0 +tablib==3.6.1 tzdata==2024.1 From b7668fbfc3a81abf2c6a8ace98047647fd9244c9 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 4 Apr 2024 16:23:16 -0400 Subject: [PATCH 030/303] PRVB --- docs/release-notes/version-3.7.md | 4 ++++ netbox/netbox/settings.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index ddbeb4bca..cc20c9860 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -1,5 +1,9 @@ # NetBox v3.7 +## v3.7.6 (FUTURE) + +--- + ## v3.7.5 (2024-04-04) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 0ae43da66..943032253 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -28,7 +28,7 @@ from netbox.plugins import PluginConfig # Environment setup # -VERSION = '3.7.5' +VERSION = '3.7.6-dev' # Hostname HOSTNAME = platform.node() From 54c6d95fbb45f39df4c55a6740e84880600afc06 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 11 Apr 2024 13:15:05 -0700 Subject: [PATCH 031/303] 15654 check for no termination in TunnelTerminationSerializer --- netbox/vpn/api/serializers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/netbox/vpn/api/serializers.py b/netbox/vpn/api/serializers.py index 5f6fcd5f7..36ccf28de 100644 --- a/netbox/vpn/api/serializers.py +++ b/netbox/vpn/api/serializers.py @@ -98,6 +98,9 @@ class TunnelTerminationSerializer(NetBoxModelSerializer): @extend_schema_field(serializers.JSONField(allow_null=True)) def get_termination(self, obj): + if not obj.termination: + return None + serializer = get_serializer_for_model(obj.termination, prefix=NESTED_SERIALIZER_PREFIX) context = {'request': self.context['request']} return serializer(obj.termination, context=context).data From d7922a68d88b79b32b710e3b92476e4500a4452f Mon Sep 17 00:00:00 2001 From: Julio-Oliveira-Encora Date: Thu, 11 Apr 2024 17:15:12 -0300 Subject: [PATCH 032/303] Fixed line 391 in netbox/virtualization/views.py. It was reeplaced "view_virtual_disk" with "view_virtualdisk" --- netbox/virtualization/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 6019fc227..ec19a1d22 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -388,7 +388,7 @@ class VirtualMachineVirtualDisksView(generic.ObjectChildrenView): tab = ViewTab( label=_('Virtual Disks'), badge=lambda obj: obj.virtual_disk_count, - permission='virtualization.view_virtual_disk', + permission='virtualization.view_virtualdisk', weight=500 ) actions = { From 5098422f68fcb73e2fd64dbb55551c5069b0ed80 Mon Sep 17 00:00:00 2001 From: Julio Oliveira at Encora <149191228+Julio-Oliveira-Encora@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:19:15 -0300 Subject: [PATCH 033/303] Fixes #15644 - Add the ability to configure HSTS in NetBox (#15683) * Added SECURE_HSTS_SECONDSm SECURE_HSTS_INCLUDE_SUBDOMAINS, and SECURE_HSTS_PRELOAD to settings.py * Addressed some PR comments. * Apply suggestions from code review --------- Co-authored-by: Jeremy Stretch --- docs/configuration/security.md | 24 ++++++++++++++++++++++++ netbox/netbox/settings.py | 3 +++ 2 files changed, 27 insertions(+) diff --git a/docs/configuration/security.md b/docs/configuration/security.md index 2ae92285f..9de09ceda 100644 --- a/docs/configuration/security.md +++ b/docs/configuration/security.md @@ -183,6 +183,30 @@ The view name or URL to which a user is redirected after logging out. --- +## SECURE_HSTS_INCLUDE_SUBDOMAINS + +Default: False + +If true, the `includeSubDomains` directive will be included in the HTTP Strict Transport Security (HSTS) header. This directive instructs the browser to apply the HSTS policy to all subdomains of the current domain. + +--- + +## SECURE_HSTS_PRELOAD + +Default: False + +If true, the `preload` directive will be included in the HTTP Strict Transport Security (HSTS) header. This directive instructs the browser to preload the site in HTTPS. Browsers that use the HSTS preload list will force the site to be accessed via HTTPS even if the user types HTTP in the address bar. + +--- + +## SECURE_HSTS_SECONDS + +Default: 0 + +If set to a non-zero integer value, the SecurityMiddleware sets the HTTP Strict Transport Security (HSTS) header on all responses that do not already have it. This will instruct the browser that the website must be accessed via HTTPS, blocking any HTTP request. + +--- + ## SECURE_SSL_REDIRECT Default: False diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 943032253..55002aa87 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -160,6 +160,9 @@ RQ_RETRY_INTERVAL = getattr(configuration, 'RQ_RETRY_INTERVAL', 60) RQ_RETRY_MAX = getattr(configuration, 'RQ_RETRY_MAX', 0) SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/') SEARCH_BACKEND = getattr(configuration, 'SEARCH_BACKEND', 'netbox.search.backends.CachedValueSearchBackend') +SECURE_HSTS_INCLUDE_SUBDOMAINS = getattr(configuration, 'SECURE_HSTS_INCLUDE_SUBDOMAINS', False) +SECURE_HSTS_PRELOAD = getattr(configuration, 'SECURE_HSTS_PRELOAD', False) +SECURE_HSTS_SECONDS = getattr(configuration, 'SECURE_HSTS_SECONDS', 0) SECURE_SSL_REDIRECT = getattr(configuration, 'SECURE_SSL_REDIRECT', False) SENTRY_DSN = getattr(configuration, 'SENTRY_DSN', None) SENTRY_ENABLED = getattr(configuration, 'SENTRY_ENABLED', False) From f7e4fe2a9c4b8d4441cce342af616df6367041ab Mon Sep 17 00:00:00 2001 From: "Wrage, Florian" Date: Tue, 9 Apr 2024 15:48:49 +0200 Subject: [PATCH 034/303] Fixes #15640: add identifier field to search index of l2vpn --- netbox/vpn/search.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/vpn/search.py b/netbox/vpn/search.py index 066bc68bb..c1914dc22 100644 --- a/netbox/vpn/search.py +++ b/netbox/vpn/search.py @@ -75,6 +75,7 @@ class L2VPNIndex(SearchIndex): fields = ( ('name', 100), ('slug', 110), + ('identifier', 200), ('description', 500), ('comments', 5000), ) From f47b15886326e61cb2c4f39a4742185772bcbc4e Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Mon, 15 Apr 2024 08:24:32 -0700 Subject: [PATCH 035/303] 15685 Allow decimal for cable length filter form (#15703) * 15685 allow decimal for cable length filter * 15685 allow decimal for cable length filter * 15685 remove minlenth * 15685 remove minlenth --- netbox/dcim/forms/filtersets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 95c441381..d8d326271 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -977,9 +977,9 @@ class CableFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm): label=_('Color'), required=False ) - length = forms.IntegerField( + length = forms.DecimalField( label=_('Length'), - required=False + required=False, ) length_unit = forms.ChoiceField( label=_('Length unit'), From 17e8773c8cf13184f26c8a58b77407899adbc640 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 15 Apr 2024 12:10:33 -0400 Subject: [PATCH 036/303] Changelog for #15640, #15644, #15654, #15668, #15685 --- docs/release-notes/version-3.7.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index cc20c9860..48afc6688 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -2,6 +2,17 @@ ## v3.7.6 (FUTURE) +### Enhancements + +* [#15640](https://github.com/netbox-community/netbox/issues/15640) - Add global search support for L2VPN identifiers +* [#15644](https://github.com/netbox-community/netbox/issues/15644) - Introduce new configuration parameters for enabling HTTP Strict Transport Security (HSTS) + +### Bug Fixes + +* [#15654](https://github.com/netbox-community/netbox/issues/15654) - Fix `AttributeError` exception when attempting to save an incomplete tunnel termination +* [#15668](https://github.com/netbox-community/netbox/issues/15668) - Fix permission required to display virtual disks tab on virtual machine UI view +* [#15685](https://github.com/netbox-community/netbox/issues/15685) - Allow filtering cables by decimal values using UI filter form + --- ## v3.7.5 (2024-04-04) From 3c3943c809aa16499d603acdd160fe431bd8720c Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 15 Apr 2024 12:12:35 -0400 Subject: [PATCH 037/303] Convert "needs triage" label to a status indicator --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/documentation_change.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- .github/workflows/auto-assign-issue.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index d4a9bab72..60d9a7091 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -1,7 +1,7 @@ --- name: 🐛 Bug Report description: Report a reproducible bug in the current release of NetBox -labels: ["type: bug", "needs triage"] +labels: ["type: bug", "status: needs triage"] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/documentation_change.yaml b/.github/ISSUE_TEMPLATE/documentation_change.yaml index 0f80f1716..b5a970782 100644 --- a/.github/ISSUE_TEMPLATE/documentation_change.yaml +++ b/.github/ISSUE_TEMPLATE/documentation_change.yaml @@ -1,7 +1,7 @@ --- name: 📖 Documentation Change description: Suggest an addition or modification to the NetBox documentation -labels: ["type: documentation", "needs triage"] +labels: ["type: documentation", "status: needs triage"] body: - type: dropdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 2cee040f8..22c65f276 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -1,7 +1,7 @@ --- name: ✨ Feature Request description: Propose a new NetBox feature or enhancement -labels: ["type: feature", "needs triage"] +labels: ["type: feature", "status: needs triage"] body: - type: markdown attributes: diff --git a/.github/workflows/auto-assign-issue.yml b/.github/workflows/auto-assign-issue.yml index 9abbc0cce..e32e23c84 100644 --- a/.github/workflows/auto-assign-issue.yml +++ b/.github/workflows/auto-assign-issue.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: pozil/auto-assign-issue@v1 - if: "contains(github.event.issue.labels.*.name, 'needs triage')" + if: "contains(github.event.issue.labels.*.name, 'status: needs triage')" with: # Weighted assignments assignees: arthanson:3, jeffgdotorg:3, jeremystretch:3, abhi1693, DanSheps From 815cab5c9a1a47fde22251411abca885669c067a Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 26 Mar 2024 07:28:41 -0700 Subject: [PATCH 038/303] 15532 fix autotype_decorator for method fields --- netbox/netbox/graphql/filter_mixins.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/netbox/netbox/graphql/filter_mixins.py b/netbox/netbox/graphql/filter_mixins.py index 3103b06c6..bfb958563 100644 --- a/netbox/netbox/graphql/filter_mixins.py +++ b/netbox/netbox/graphql/filter_mixins.py @@ -4,6 +4,7 @@ from typing import List import django_filters import strawberry import strawberry_django +from django.core.exceptions import FieldDoesNotExist from strawberry import auto from ipam.fields import ASNField from netbox.graphql.scalars import BigInt @@ -164,7 +165,11 @@ def autotype_decorator(filterset): should_create_function = False attr_type = auto if fieldname not in cls.__annotations__: - field = model._meta.get_field(fieldname) + try: + field = model._meta.get_field(fieldname) + except FieldDoesNotExist: + continue + if isinstance(field, CounterCacheField): should_create_function = True attr_type = BigInt | None From 5e05041b8b93c0f447a48f7b0a1ce35459188899 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Mon, 15 Apr 2024 10:22:56 -0700 Subject: [PATCH 039/303] 15671 save module before sync_classes (#15675) * 15671 save module before sync_classes * 15671 don't return save --- netbox/extras/models/scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/extras/models/scripts.py b/netbox/extras/models/scripts.py index 551a8b4f0..b0344443c 100644 --- a/netbox/extras/models/scripts.py +++ b/netbox/extras/models/scripts.py @@ -165,8 +165,8 @@ class ScriptModule(PythonModuleMixin, JobsMixin, ManagedFile): def save(self, *args, **kwargs): self.file_root = ManagedFileRootPathChoices.SCRIPTS + super().save(*args, **kwargs) self.sync_classes() - return super().save(*args, **kwargs) @receiver(post_save, sender=ScriptModule) From 0da8164600c12a92222824e7930045a760f46cd9 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Mon, 15 Apr 2024 10:29:29 -0700 Subject: [PATCH 040/303] 15684 strawberry filter (#15686) * 15579 update requirements * 15684 add USE_DEPRECATED_FILTERS to strawberry --- netbox/netbox/settings.py | 1 + requirements.txt | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 18feee262..32daa137c 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -725,6 +725,7 @@ if not ENABLE_LOCALIZATION: # STRAWBERRY_DJANGO = { "TYPE_DESCRIPTION_FROM_MODEL_DOCSTRING": True, + "USE_DEPRECATED_FILTERS": True, } # diff --git a/requirements.txt b/requirements.txt index fed2d8f13..033ed862a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,8 +29,8 @@ PyYAML==6.0.1 requests==2.31.0 social-auth-app-django==5.4.0 social-auth-core[openidconnect]==4.5.3 -strawberry-graphql==0.222.0 -strawberry-graphql-django==0.34.0 +strawberry-graphql==0.224.1 +strawberry-graphql-django==0.37.0 svgwrite==1.4.3 tablib==3.6.0 tzdata==2024.1 From 4e4c2777113595273275255486a27c2246a7aec6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 15 Apr 2024 14:40:33 -0400 Subject: [PATCH 041/303] Fixes #15652: Fix the display of error messages after attempting to delete an object --- netbox/utilities/htmx.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/netbox/utilities/htmx.py b/netbox/utilities/htmx.py index 91a2c5ada..55e3b2616 100644 --- a/netbox/utilities/htmx.py +++ b/netbox/utilities/htmx.py @@ -2,12 +2,10 @@ __all__ = ( 'htmx_partial', ) -PAGE_CONTAINER_ID = 'page-content' - def htmx_partial(request): """ Determines whether to render partial (versus complete) HTML content in response to an HTMX request, based on the target element. """ - return request.htmx and request.htmx.target and request.htmx.target != PAGE_CONTAINER_ID + return request.htmx and not request.htmx.boosted From 4562e347fdc666d45d954ba3015915bad222b18e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 16 Apr 2024 10:45:39 -0400 Subject: [PATCH 042/303] Fixes #15613: Show login button/user menu on mobile view --- netbox/project-static/dist/netbox.css | Bin 552689 -> 552769 bytes .../styles/overrides/_bootstrap.scss | 6 ++ netbox/templates/base/layout.html | 57 +++--------------- netbox/templates/inc/user_menu.html | 50 +++++++++++++++ 4 files changed, 66 insertions(+), 47 deletions(-) create mode 100644 netbox/templates/inc/user_menu.html diff --git a/netbox/project-static/dist/netbox.css b/netbox/project-static/dist/netbox.css index c4b712c863e9d9e4376367697558d196d1a8bb4b..7cf2533fc228e74ecd494d1d8c303cdddef45e6c 100644 GIT binary patch delta 95 zcmex(SMlII#fBEf7N!>F7M2#)7Pc1lEgUD}G%||xQi}2mQu535bW<|RGE-8EbTCBp lGK!`bq%(_7Z&YCyW-2I}{%}9D@bm)*nYg#Vh~wZC007>tBd!1d delta 44 zcmX?jPx0ej#fBEf7N!>F7M2#)7Pc1lEgUD}m
    - {# Header icon #} + {# Menu toggle (mobile view) #} + + {# Logo #}

    - {% trans + {% trans

    + {# User menu (mobile view) #} + + {# Navigation menu #}
    From db87fe96b78d172a786903527e718079a378f03a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 19 Apr 2024 15:23:09 -0400 Subject: [PATCH 069/303] Clean up bulk import view --- netbox/netbox/views/generic/bulk_views.py | 19 ++- netbox/templates/generic/bulk_import.html | 160 +++++++++++----------- 2 files changed, 94 insertions(+), 85 deletions(-) diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index d609f0a18..be574204c 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -314,11 +314,20 @@ class BulkImportView(GetReturnURLMixin, BaseMultiObjectView): return data def _get_form_fields(self): - # Exclude any fields which use a HiddenInput widget - return { - name: field for name, field in self.model_form().fields.items() - if type(field.widget) is not HiddenInput - } + form = self.model_form() + required_fields = {} + optional_fields = {} + + # Return only visible fields, with required fields listed first + for field in form.visible_fields(): + if field.is_hidden: + continue + elif field.field.required: + required_fields[field.name] = field.field + else: + optional_fields[field.name] = field.field + + return {**required_fields, **optional_fields} def _save_object(self, import_form, model_form, request): diff --git a/netbox/templates/generic/bulk_import.html b/netbox/templates/generic/bulk_import.html index 484b7a36e..d4e61e675 100644 --- a/netbox/templates/generic/bulk_import.html +++ b/netbox/templates/generic/bulk_import.html @@ -103,87 +103,87 @@ Context: {% if fields %}
    -
    -
    -
    {% trans "Field Options" %}
    - - - - - - - - {% for name, field in fields.items %} - - - - - - - {% endfor %} -
    {% trans "Field" %}{% trans "Required" %}{% trans "Accessor" %}{% trans "Description" %}
    - {% if field.required %}{% endif %}{{ name }}{% if field.required %}{% endif %} - - {% if field.required %} - {% checkmark True true="Required" %} - {% else %} - {{ ''|placeholder }} - {% endif %} - - {% if field.to_field_name %} - {{ field.to_field_name }} - {% else %} - {{ ''|placeholder }} - {% endif %} - - {% if field.STATIC_CHOICES %} - - - {% endif %} - {% if field.help_text %} - {{ field.help_text }}
    - {% elif field.label %} - {{ field.label }}
    - {% endif %} - {% if field|widget_type == 'dateinput' %} - {% trans "Format: YYYY-MM-DD" %} - {% elif field|widget_type == 'checkboxinput' %} - {% trans "Specify true or false" %} - {% endif %} -
    -
    +
    +
    +
    {% trans "Field Options" %}
    + + + + + + + + + + + {% for name, field in fields.items %} + + + + {% if field.to_field_name %} + + {% else %} + + {% endif %} + + + {% endfor %} + +
    {% trans "Field" %}{% trans "Required" %}{% trans "Accessor" %}{% trans "Description" %}
    + {{ name }} + + {% if field.required %} + {% checkmark True true="Required" %} + {% else %} + {{ ''|placeholder }} + {% endif %} + {{ field.to_field_name }}{{ ''|placeholder }} + {% if field.help_text %} + {{ field.help_text }} + {% elif field.label %} + {{ field.label }} + {% endif %} + {% if field.STATIC_CHOICES %} + + + {% endif %} + {% if field|widget_type == 'dateinput' %} +
    {% trans "Format: YYYY-MM-DD" %} + {% elif field|widget_type == 'checkboxinput' %} +
    {% trans "Specify true or false" %} + {% endif %} +
    +

    From f42d0336c26634cd54e5b3bda2f4f0602a4c0a91 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 19 Apr 2024 15:27:25 -0400 Subject: [PATCH 070/303] Clean up layout of global search results --- netbox/templates/search.html | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/netbox/templates/search.html b/netbox/templates/search.html index e56b8f379..0ce0c0299 100644 --- a/netbox/templates/search.html +++ b/netbox/templates/search.html @@ -17,7 +17,7 @@ {% endblock tabs %} {% block content %} -

    +
    {% render_form form %} @@ -29,10 +29,12 @@
    -
    -
    -
    - {% include 'htmx/table.html' %} +
    +
    +
    +
    + {% include 'htmx/table.html' %} +
    From 3d3c1c315b46a7926356301a7ad93fc0b5d69e95 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 19 Apr 2024 16:15:32 -0400 Subject: [PATCH 071/303] Update documentation for the DEFAULT_LANGUAGE configuration parameter --- docs/configuration/system.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/configuration/system.md b/docs/configuration/system.md index 806839778..28c09444b 100644 --- a/docs/configuration/system.md +++ b/docs/configuration/system.md @@ -16,10 +16,7 @@ BASE_PATH = 'netbox/' Default: `en-us` (US English) -Defines the default preferred language/locale for requests that do not specify one. This is used to alter e.g. the display of dates and numbers to fit the user's locale. See [this list](http://www.i18nguy.com/unicode/language-identifiers.html) of standard language codes. (This parameter maps to Django's [`LANGUAGE_CODE`](https://docs.djangoproject.com/en/stable/ref/settings/#language-code) internal setting.) - -!!! note - Altering this parameter will *not* change the language used in NetBox. We hope to provide translation support in a future NetBox release. +Defines the default preferred language/locale for requests that do not specify one. (This parameter maps to Django's [`LANGUAGE_CODE`](https://docs.djangoproject.com/en/stable/ref/settings/#language-code) internal setting.) --- From 94c31622acf4978720e9dda9592094ad5d5161b9 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 18 Apr 2024 14:59:21 -0700 Subject: [PATCH 072/303] 15588 set readonly nullable fields as allow_null=True --- netbox/dcim/api/serializers.py | 8 ++++---- netbox/ipam/api/serializers.py | 10 +++++----- netbox/virtualization/api/serializers.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 053b3e9ea..ce3cf4d9c 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -612,7 +612,7 @@ class InventoryItemTemplateSerializer(ValidatedModelSerializer): required=False, allow_null=True ) - component = serializers.SerializerMethodField(read_only=True) + component = serializers.SerializerMethodField(read_only=True, allow_null=True) _depth = serializers.IntegerField(source='level', read_only=True) class Meta: @@ -685,7 +685,7 @@ class DeviceSerializer(NetBoxModelSerializer): ) status = ChoiceField(choices=DeviceStatusChoices, required=False) airflow = ChoiceField(choices=DeviceAirflowChoices, allow_blank=True, required=False) - primary_ip = NestedIPAddressSerializer(read_only=True) + primary_ip = NestedIPAddressSerializer(read_only=True, allow_null=True) primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True) primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True) oob_ip = NestedIPAddressSerializer(required=False, allow_null=True) @@ -735,7 +735,7 @@ class DeviceSerializer(NetBoxModelSerializer): class DeviceWithConfigContextSerializer(DeviceSerializer): - config_context = serializers.SerializerMethodField(read_only=True) + config_context = serializers.SerializerMethodField(read_only=True, allow_null=True) class Meta(DeviceSerializer.Meta): fields = [ @@ -1067,7 +1067,7 @@ class InventoryItemSerializer(NetBoxModelSerializer): required=False, allow_null=True ) - component = serializers.SerializerMethodField(read_only=True) + component = serializers.SerializerMethodField(read_only=True, allow_null=True) _depth = serializers.IntegerField(source='level', read_only=True) class Meta: diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index 33aa55a93..8dca73d94 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -262,7 +262,7 @@ class AvailableVLANSerializer(serializers.Serializer): Representation of a VLAN which does not exist in the database. """ vid = serializers.IntegerField(read_only=True) - group = NestedVLANGroupSerializer(read_only=True) + group = NestedVLANGroupSerializer(read_only=True, allow_null=True) def to_representation(self, instance): return { @@ -348,9 +348,9 @@ class AvailablePrefixSerializer(serializers.Serializer): """ Representation of a prefix which does not exist in the database. """ - family = serializers.IntegerField(read_only=True) + family = serializers.IntegerField(read_only=True, allow_null=True) prefix = serializers.CharField(read_only=True) - vrf = NestedVRFSerializer(read_only=True) + vrf = NestedVRFSerializer(read_only=True, allow_null=True) def to_representation(self, instance): if self.context.get('vrf'): @@ -429,9 +429,9 @@ class AvailableIPSerializer(serializers.Serializer): """ Representation of an IP address which does not exist in the database. """ - family = serializers.IntegerField(read_only=True) + family = serializers.IntegerField(read_only=True, allow_null=True) address = serializers.CharField(read_only=True) - vrf = NestedVRFSerializer(read_only=True) + vrf = NestedVRFSerializer(read_only=True, allow_null=True) description = serializers.CharField(required=False) def to_representation(self, instance): diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py index 34e4037e9..a54643e62 100644 --- a/netbox/virtualization/api/serializers.py +++ b/netbox/virtualization/api/serializers.py @@ -76,7 +76,7 @@ class VirtualMachineSerializer(NetBoxModelSerializer): role = NestedDeviceRoleSerializer(required=False, allow_null=True) tenant = NestedTenantSerializer(required=False, allow_null=True) platform = NestedPlatformSerializer(required=False, allow_null=True) - primary_ip = NestedIPAddressSerializer(read_only=True) + primary_ip = NestedIPAddressSerializer(read_only=True, allow_null=True) primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True) primary_ip6 = NestedIPAddressSerializer(required=False, allow_null=True) config_template = NestedConfigTemplateSerializer(required=False, allow_null=True, default=None) From c9de3128ca156000d70a38ff948c78603c02ea9a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 19 Apr 2024 16:10:06 -0400 Subject: [PATCH 073/303] Fixes #15790: Fix live preview support for EventRule comments --- netbox/extras/forms/model_forms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index 8f9face41..70b7a78a4 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -265,6 +265,7 @@ class EventRuleForm(NetBoxModelForm): required=False, help_text=_('Enter parameters to pass to the action in JSON format.') ) + comments = CommentField() fieldsets = ( (_('Event Rule'), ('name', 'description', 'content_types', 'enabled', 'tags')), From 88facbafbb6a754cb5f48fb541005bdabda744b6 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Fri, 19 Apr 2024 14:09:55 -0700 Subject: [PATCH 074/303] 15761 filter IKE Proposals on IKE Policy detail view (#15766) * 15761 filter IKEAProposals on IKEAPolicy detail view * Add test for ike_policy filter --------- Co-authored-by: Jeremy Stretch --- netbox/vpn/filtersets.py | 11 +++++++++++ netbox/vpn/tests/test_filtersets.py | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/netbox/vpn/filtersets.py b/netbox/vpn/filtersets.py index 0647838a8..10f0834fb 100644 --- a/netbox/vpn/filtersets.py +++ b/netbox/vpn/filtersets.py @@ -136,6 +136,17 @@ class IKEProposalFilterSet(NetBoxModelFilterSet): group = django_filters.MultipleChoiceFilter( choices=DHGroupChoices ) + ike_policy_id = django_filters.ModelMultipleChoiceFilter( + field_name='ike_policies', + queryset=IKEPolicy.objects.all(), + label=_('IKE policy (ID)'), + ) + ike_policy = django_filters.ModelMultipleChoiceFilter( + field_name='ike_policies__name', + queryset=IKEPolicy.objects.all(), + to_field_name='name', + label=_('IKE policy (name)'), + ) class Meta: model = IKEProposal diff --git a/netbox/vpn/tests/test_filtersets.py b/netbox/vpn/tests/test_filtersets.py index d4e80750d..f11e63f10 100644 --- a/netbox/vpn/tests/test_filtersets.py +++ b/netbox/vpn/tests/test_filtersets.py @@ -331,6 +331,16 @@ class IKEProposalTestCase(TestCase, ChangeLoggedFilterSetTests): ) IKEProposal.objects.bulk_create(ike_proposals) + ike_policies = ( + IKEPolicy(name='IKE Policy 1'), + IKEPolicy(name='IKE Policy 2'), + IKEPolicy(name='IKE Policy 3'), + ) + IKEPolicy.objects.bulk_create(ike_policies) + ike_policies[0].proposals.add(ike_proposals[0]) + ike_policies[1].proposals.add(ike_proposals[1]) + ike_policies[2].proposals.add(ike_proposals[2]) + def test_q(self): params = {'q': 'foobar1'} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) @@ -369,6 +379,13 @@ class IKEProposalTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'sa_lifetime': [1000, 2000]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + def test_ike_policy(self): + ike_policies = IKEPolicy.objects.all()[:2] + params = {'ike_policy_id': [ike_policies[0].pk, ike_policies[1].pk]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'ike_policy': [ike_policies[0].name, ike_policies[1].name]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class IKEPolicyTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = IKEPolicy.objects.all() From 781409b5ae4f984124de042d483b7262dc0a7446 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 19 Apr 2024 15:42:52 -0400 Subject: [PATCH 075/303] Fixes #15787: Convert User ID column to 64-bit integer --- netbox/users/migrations/0005_alter_user_table.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/netbox/users/migrations/0005_alter_user_table.py b/netbox/users/migrations/0005_alter_user_table.py index 22c6bdd42..62cd3849d 100644 --- a/netbox/users/migrations/0005_alter_user_table.py +++ b/netbox/users/migrations/0005_alter_user_table.py @@ -33,6 +33,9 @@ class Migration(migrations.Migration): table=None, ), + # Convert the `id` column to a 64-bit integer (BigAutoField is implied by DEFAULT_AUTO_FIELD) + migrations.RunSQL("ALTER TABLE users_user ALTER COLUMN id TYPE bigint"), + # Rename auth_user_* sequences migrations.RunSQL("ALTER TABLE auth_user_groups_id_seq RENAME TO users_user_groups_id_seq"), migrations.RunSQL("ALTER TABLE auth_user_id_seq RENAME TO users_user_id_seq"), From 781d932b2ab9b2184271f4c0c8bb6d649cf41514 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 19 Apr 2024 15:22:09 -0700 Subject: [PATCH 076/303] 15789 make sure job completed before including config_form --- netbox/templates/extras/script_result.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/templates/extras/script_result.html b/netbox/templates/extras/script_result.html index ee1127c8a..ccc7f4d23 100644 --- a/netbox/templates/extras/script_result.html +++ b/netbox/templates/extras/script_result.html @@ -101,5 +101,7 @@ {% endblock content %} {% block modals %} + {% if job.completed %} {% table_config_form table table_name="ObjectTable" %} + {% endif %} {% endblock modals %} From 90d0104359a07f25185fc238d820005780ace6c4 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Mon, 22 Apr 2024 05:22:53 -0700 Subject: [PATCH 077/303] 15541 Add component selector to InventoryItemTemplate (#15759) * 15541 make inventoryitemtemplateform match inventoryitemform * 15541 set tab active --- netbox/dcim/forms/model_forms.py | 105 ++++++++++++++++-- netbox/dcim/views.py | 2 + .../dcim/inventoryitemtemplate_edit.html | 104 +++++++++++++++++ 3 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 netbox/templates/dcim/inventoryitemtemplate_edit.html diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 6773bc55f..cee8fcfba 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -976,21 +976,67 @@ class InventoryItemTemplateForm(ComponentTemplateForm): queryset=Manufacturer.objects.all(), required=False ) - component_type = ContentTypeChoiceField( - queryset=ContentType.objects.all(), - limit_choices_to=MODULAR_COMPONENT_TEMPLATE_MODELS, + # Assigned component selectors + consoleporttemplate = DynamicModelChoiceField( + queryset=ConsolePortTemplate.objects.all(), required=False, - widget=forms.HiddenInput + query_params={ + 'device_type_id': '$device_type' + }, + label=_('Console port template') ) - component_id = forms.IntegerField( + consoleserverporttemplate = DynamicModelChoiceField( + queryset=ConsoleServerPortTemplate.objects.all(), required=False, - widget=forms.HiddenInput + query_params={ + 'device_type_id': '$device_type' + }, + label=_('Console server port template') + ) + frontporttemplate = DynamicModelChoiceField( + queryset=FrontPortTemplate.objects.all(), + required=False, + query_params={ + 'device_type_id': '$device_type' + }, + label=_('Front port template') + ) + interfacetemplate = DynamicModelChoiceField( + queryset=InterfaceTemplate.objects.all(), + required=False, + query_params={ + 'device_type_id': '$device_type' + }, + label=_('Interface template') + ) + poweroutlettemplate = DynamicModelChoiceField( + queryset=PowerOutletTemplate.objects.all(), + required=False, + query_params={ + 'device_type_id': '$device_type' + }, + label=_('Power outlet template') + ) + powerporttemplate = DynamicModelChoiceField( + queryset=PowerPortTemplate.objects.all(), + required=False, + query_params={ + 'device_type_id': '$device_type' + }, + label=_('Power port template') + ) + rearporttemplate = DynamicModelChoiceField( + queryset=RearPortTemplate.objects.all(), + required=False, + query_params={ + 'device_type_id': '$device_type' + }, + label=_('Rear port template') ) fieldsets = ( (None, ( 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'description', - 'component_type', 'component_id', )), ) @@ -998,9 +1044,52 @@ class InventoryItemTemplateForm(ComponentTemplateForm): model = InventoryItemTemplate fields = [ 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'description', - 'component_type', 'component_id', ] + def __init__(self, *args, **kwargs): + instance = kwargs.get('instance') + initial = kwargs.get('initial', {}).copy() + component_type = initial.get('component_type') + component_id = initial.get('component_id') + + # Used for picking the default active tab for component selection + self.no_component = True + + if instance: + # When editing set the initial value for component selection + for component_model in ContentType.objects.filter(MODULAR_COMPONENT_TEMPLATE_MODELS): + if type(instance.component) is component_model.model_class(): + initial[component_model.model] = instance.component + self.no_component = False + break + elif component_type and component_id: + # When adding the InventoryItem from a component page + if content_type := ContentType.objects.filter(MODULAR_COMPONENT_TEMPLATE_MODELS).filter(pk=component_type).first(): + if component := content_type.model_class().objects.filter(pk=component_id).first(): + initial[content_type.model] = component + self.no_component = False + + kwargs['initial'] = initial + + super().__init__(*args, **kwargs) + + def clean(self): + super().clean() + + # Handle object assignment + selected_objects = [ + field for field in ( + 'consoleporttemplate', 'consoleserverporttemplate', 'frontporttemplate', 'interfacetemplate', + 'poweroutlettemplate', 'powerporttemplate', 'rearporttemplate' + ) if self.cleaned_data[field] + ] + if len(selected_objects) > 1: + raise forms.ValidationError(_("An InventoryItem can only be assigned to a single component.")) + elif selected_objects: + self.instance.component = self.cleaned_data[selected_objects[0]] + else: + self.instance.component = None + # # Device components diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index d0e92ff56..ce4bb5750 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1656,6 +1656,7 @@ class InventoryItemTemplateCreateView(generic.ComponentCreateView): queryset = InventoryItemTemplate.objects.all() form = forms.InventoryItemTemplateCreateForm model_form = forms.InventoryItemTemplateForm + template_name = 'dcim/inventoryitemtemplate_edit.html' def alter_object(self, instance, request): # Set component (if any) @@ -1673,6 +1674,7 @@ class InventoryItemTemplateCreateView(generic.ComponentCreateView): class InventoryItemTemplateEditView(generic.ObjectEditView): queryset = InventoryItemTemplate.objects.all() form = forms.InventoryItemTemplateForm + template_name = 'dcim/inventoryitemtemplate_edit.html' @register_model_view(InventoryItemTemplate, 'delete') diff --git a/netbox/templates/dcim/inventoryitemtemplate_edit.html b/netbox/templates/dcim/inventoryitemtemplate_edit.html new file mode 100644 index 000000000..d3ac58e25 --- /dev/null +++ b/netbox/templates/dcim/inventoryitemtemplate_edit.html @@ -0,0 +1,104 @@ +{% extends 'generic/object_edit.html' %} +{% load static %} +{% load form_helpers %} +{% load helpers %} +{% load i18n %} + +{% block form %} +
    +
    +
    {% trans "Inventory Item" %}
    +
    + {% render_field form.device_type %} + {% render_field form.parent %} + {% render_field form.name %} + {% render_field form.label %} + {% render_field form.role %} + {% render_field form.description %} +
    + +
    +
    +
    {% trans "Hardware" %}
    +
    + {% render_field form.manufacturer %} + {% render_field form.part_id %} +
    + +
    +
    +
    {% trans "Component Assignment" %}
    +
    +
    + +
    +
    +
    + {% render_field form.consoleporttemplate %} +
    +
    + {% render_field form.consoleserverporttemplate %} +
    +
    + {% render_field form.frontporttemplate %} +
    +
    + {% render_field form.interfacetemplate %} +
    +
    + {% render_field form.poweroutlettemplate %} +
    +
    + {% render_field form.powerporttemplate %} +
    +
    + {% render_field form.rearporttemplate %} +
    +
    +
    + + {% if form.custom_fields %} +
    +
    +
    {% trans "Custom Fields" %}
    +
    + {% render_custom_fields form %} +
    + {% endif %} +{% endblock %} From b6e38b2ebe0717b3a7606d36f5bc9b40444fb42c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markku=20Leini=C3=B6?= Date: Mon, 22 Apr 2024 16:25:16 +0300 Subject: [PATCH 078/303] Closes #14690: Pretty-format JSON fields in the config form (#15623) * Closes #14690: Pretty-format JSON fields in the config form * Revert changes * Use our own JSONField for config parameters for pretty editor outputs * Compare identity instead of equality --- netbox/core/forms/model_forms.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/netbox/core/forms/model_forms.py b/netbox/core/forms/model_forms.py index ae891dd59..0f4f971dc 100644 --- a/netbox/core/forms/model_forms.py +++ b/netbox/core/forms/model_forms.py @@ -3,6 +3,7 @@ import json from django import forms from django.conf import settings +from django.forms.fields import JSONField as _JSONField from django.utils.translation import gettext_lazy as _ from core.forms.mixins import SyncedDataMixin @@ -12,7 +13,7 @@ from netbox.forms import NetBoxModelForm from netbox.registry import registry from netbox.utils import get_data_backend_choices from utilities.forms import BootstrapMixin, get_field_value -from utilities.forms.fields import CommentField +from utilities.forms.fields import CommentField, JSONField from utilities.forms.widgets import HTMXSelect __all__ = ( @@ -132,6 +133,9 @@ class ConfigFormMetaclass(forms.models.ModelFormMetaclass): 'help_text': param.description, } field_kwargs.update(**param.field_kwargs) + if param.field is _JSONField: + # Replace with our own JSONField to get pretty JSON in config editor + param.field = JSONField param_fields[param.name] = param.field(**field_kwargs) attrs.update(param_fields) From ebe504c8252ca29cbdbc0a0bbf2ee0e0167fffcd Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 09:52:03 -0400 Subject: [PATCH 079/303] Closes #15664: Restore usage of READTHEDOCS env variable --- docs/_theme/main.html | 4 ++-- mkdocs.yml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/_theme/main.html b/docs/_theme/main.html index 3ff44b9cb..4dfc4e14e 100644 --- a/docs/_theme/main.html +++ b/docs/_theme/main.html @@ -2,8 +2,8 @@ {% block site_meta %} {{ super() }} - {# Disable search indexing unless we're building for ReadTheDocs (see #10496) #} - {% if page.canonical_url != 'https://docs.netbox.dev/' %} + {# Disable search indexing unless we're building for ReadTheDocs #} + {% if not config.extra.readthedocs %} {% endif %} {% endblock %} diff --git a/mkdocs.yml b/mkdocs.yml index c04ef519f..5aa657230 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -42,6 +42,7 @@ plugins: show_root_toc_entry: false show_source: false extra: + readthedocs: !ENV READTHEDOCS social: - icon: fontawesome/brands/github link: https://github.com/netbox-community/netbox From e87877b6ea15231df1e7e10a74c69a4e1c6407aa Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 09:38:40 -0400 Subject: [PATCH 080/303] Fixes #15771: Show id field as supported on all bulk import forms --- netbox/netbox/forms/base.py | 5 ----- netbox/utilities/forms/forms.py | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/netbox/netbox/forms/base.py b/netbox/netbox/forms/base.py index 0b0e2036e..736a2fe9b 100644 --- a/netbox/netbox/forms/base.py +++ b/netbox/netbox/forms/base.py @@ -73,11 +73,6 @@ class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm): """ Base form for creating a NetBox objects from CSV data. Used for bulk importing. """ - id = forms.IntegerField( - label=_('Id'), - required=False, - help_text='Numeric ID of an existing object to update (if not creating a new object)' - ) tags = CSVModelMultipleChoiceField( label=_('Tags'), queryset=Tag.objects.all(), diff --git a/netbox/utilities/forms/forms.py b/netbox/utilities/forms/forms.py index 54c9e41cb..93227b1d0 100644 --- a/netbox/utilities/forms/forms.py +++ b/netbox/utilities/forms/forms.py @@ -70,6 +70,12 @@ class CSVModelForm(forms.ModelForm): """ ModelForm used for the import of objects in CSV format. """ + id = forms.IntegerField( + label=_('ID'), + required=False, + help_text=_('Numeric ID of an existing object to update (if not creating a new object)') + ) + def __init__(self, *args, headers=None, **kwargs): self.headers = headers or {} super().__init__(*args, **kwargs) From 6b8bfe9947db387ef00aeb026c5357400e0d23ea Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 11:25:21 -0400 Subject: [PATCH 081/303] Changelog for #14690, #15541, #15588, #15761, #15771, #15790 --- docs/release-notes/version-3.7.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index db54082dd..e42720eaf 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -4,17 +4,23 @@ ### Enhancements +* [#14690](https://github.com/netbox-community/netbox/issues/14690) - Improve rendering of JSON data in configuration form * [#15427](https://github.com/netbox-community/netbox/issues/15427) - Enable compatibility with non-Amazon S3 providers for remote data sources * [#15640](https://github.com/netbox-community/netbox/issues/15640) - Add global search support for L2VPN identifiers * [#15644](https://github.com/netbox-community/netbox/issues/15644) - Introduce new configuration parameters for enabling HTTP Strict Transport Security (HSTS) ### Bug Fixes +* [#15541](https://github.com/netbox-community/netbox/issues/15541) - Restore ability to modify assigned component template when adding/modifying an inventory item template * [#15582](https://github.com/netbox-community/netbox/issues/15582) - Fix permission constraints for synchronization of remote data sources +* [#15588](https://github.com/netbox-community/netbox/issues/15588) - Correct OpenAPI schema definitions for read-only fields which may return null values * [#15635](https://github.com/netbox-community/netbox/issues/15635) - Extend plugin removal instruction to include reindexing the global search cache * [#15654](https://github.com/netbox-community/netbox/issues/15654) - Fix `AttributeError` exception when attempting to save an incomplete tunnel termination * [#15668](https://github.com/netbox-community/netbox/issues/15668) - Fix permission required to display virtual disks tab on virtual machine UI view * [#15685](https://github.com/netbox-community/netbox/issues/15685) - Allow filtering cables by decimal values using UI filter form +* [#15761](https://github.com/netbox-community/netbox/issues/15761) - Add missing `ike_policy` & `ike_policy_id` filters for IKE proposals +* [#15771](https://github.com/netbox-community/netbox/issues/15771) - Include `id` in list of supported fields for all bulk import forms +* [#15790](https://github.com/netbox-community/netbox/issues/15790) - Fix live preview support for EventRule comments --- From 5d95d49268c2b19ad6f4850b0207897d8aa9e604 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:28:04 -0400 Subject: [PATCH 082/303] Update translations --- netbox/translations/fr/LC_MESSAGES/django.po | 9 +++++---- netbox/translations/ja/LC_MESSAGES/django.po | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/netbox/translations/fr/LC_MESSAGES/django.po b/netbox/translations/fr/LC_MESSAGES/django.po index 8c8a362ff..cc489f360 100644 --- a/netbox/translations/fr/LC_MESSAGES/django.po +++ b/netbox/translations/fr/LC_MESSAGES/django.po @@ -6,6 +6,7 @@ # Translators: # Jonathan Senecal, 2024 # Jeremy Stretch, 2024 +# Quentin Laurent, 2024 # #, fuzzy msgid "" @@ -14,7 +15,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-04 19:11+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" -"Last-Translator: Jeremy Stretch, 2024\n" +"Last-Translator: Quentin Laurent, 2024\n" "Language-Team: French (https://app.transifex.com/netbox-community/teams/178115/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -3716,7 +3717,7 @@ msgstr "Réservation" #: dcim/forms/model_forms.py:301 dcim/forms/model_forms.py:384 #: utilities/forms/fields/fields.py:47 msgid "Slug" -msgstr "limace" +msgstr "Identifiant" #: dcim/forms/model_forms.py:308 templates/dcim/devicetype.html:12 msgid "Chassis" @@ -5813,7 +5814,7 @@ msgstr "Poids maximum" #: ipam/tables/asn.py:66 netbox/navigation/menu.py:16 #: netbox/navigation/menu.py:18 msgid "Sites" -msgstr "Des sites" +msgstr "Sites" #: dcim/tests/test_api.py:49 msgid "Test case must set peer_termination_type" @@ -13355,7 +13356,7 @@ msgstr "" #: utilities/forms/fields/fields.py:48 msgid "URL-friendly unique shorthand" -msgstr "Raccourci unique et convivial pour les URL" +msgstr "Identifiant unique utilisable dans les URL" #: utilities/forms/fields/fields.py:101 msgid "Enter context data in JSON format." diff --git a/netbox/translations/ja/LC_MESSAGES/django.po b/netbox/translations/ja/LC_MESSAGES/django.po index 5b79cf0dd..34193822c 100644 --- a/netbox/translations/ja/LC_MESSAGES/django.po +++ b/netbox/translations/ja/LC_MESSAGES/django.po @@ -5,8 +5,8 @@ # # Translators: # Tatsuya Ueda , 2024 -# teapot, 2024 # Jeremy Stretch, 2024 +# teapot, 2024 # #, fuzzy msgid "" @@ -15,7 +15,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-04 19:11+0000\n" "PO-Revision-Date: 2023-10-30 17:48+0000\n" -"Last-Translator: Jeremy Stretch, 2024\n" +"Last-Translator: teapot, 2024\n" "Language-Team: Japanese (https://app.transifex.com/netbox-community/teams/178115/ja/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -7681,7 +7681,7 @@ msgstr "プレフィックス内およびプレフィックスを含む" #: ipam/filtersets.py:259 msgid "Prefixes which contain this prefix or IP" -msgstr "このプレフィックスまたは IP を含むプレフィックス" +msgstr "このプレフィックス / IP を含むプレフィックス" #: ipam/filtersets.py:270 ipam/filtersets.py:538 ipam/forms/bulk_edit.py:326 #: ipam/forms/filtersets.py:191 ipam/forms/filtersets.py:317 @@ -7700,11 +7700,11 @@ msgstr "VLAN 番号 (1-4094)" #: ipam/forms/model_forms.py:430 templates/tenancy/contact.html:54 #: tenancy/forms/bulk_edit.py:112 msgid "Address" -msgstr "住所" +msgstr "アドレス" #: ipam/filtersets.py:445 msgid "Ranges which contain this prefix or IP" -msgstr "このプレフィックスまたは IP を含む範囲" +msgstr "このプレフィックス / IP を含む範囲" #: ipam/filtersets.py:473 ipam/filtersets.py:529 msgid "Parent prefix" @@ -7743,11 +7743,11 @@ msgstr "FHRP グループ (ID)" #: ipam/filtersets.py:618 msgid "Is assigned to an interface" -msgstr "インタフェースに割り当てられている" +msgstr "インタフェースに割り当てられているか" #: ipam/filtersets.py:622 msgid "Is assigned" -msgstr "割り当てられている" +msgstr "割当済みか" #: ipam/filtersets.py:1047 msgid "IP address (ID)" @@ -7881,7 +7881,7 @@ msgstr "子 VLAN VID の最小値" #: ipam/forms/bulk_edit.py:420 msgid "Maximum child VLAN VID" -msgstr "子 VLAN VID の最大数" +msgstr "子 VLAN VID の最大値" #: ipam/forms/bulk_edit.py:428 ipam/forms/model_forms.py:531 msgid "Scope type" @@ -7905,11 +7905,11 @@ msgstr "ポート" #: ipam/forms/bulk_import.py:47 msgid "Import route targets" -msgstr "ルートターゲットをインポート" +msgstr "インポートルートターゲット" #: ipam/forms/bulk_import.py:53 msgid "Export route targets" -msgstr "ルートターゲットをエクスポートする" +msgstr "エクスポートルートターゲット" #: ipam/forms/bulk_import.py:91 ipam/forms/bulk_import.py:111 #: ipam/forms/bulk_import.py:131 From 1eca1c3d1764ff32a67283f94402b752c01be34d Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Mon, 22 Apr 2024 08:42:20 -0700 Subject: [PATCH 083/303] 15803 localize help_text (#15804) --- docs/plugins/development/forms.md | 2 +- netbox/dcim/api/serializers.py | 2 +- netbox/dcim/forms/bulk_import.py | 4 ++-- netbox/netbox/forms/base.py | 2 +- netbox/wireless/forms/bulk_import.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/plugins/development/forms.md b/docs/plugins/development/forms.md index 31751855e..1f844dc1b 100644 --- a/docs/plugins/development/forms.md +++ b/docs/plugins/development/forms.md @@ -62,7 +62,7 @@ class MyModelImportForm(NetBoxModelImportForm): site = CSVModelChoiceField( queryset=Site.objects.all(), to_field_name='name', - help_text='Assigned site' + help_text=_('Assigned site') ) class Meta: diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index ce3cf4d9c..e07d17aca 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -668,7 +668,7 @@ class DeviceSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='dcim-api:device-detail') device_type = NestedDeviceTypeSerializer() role = NestedDeviceRoleSerializer() - device_role = NestedDeviceRoleSerializer(read_only=True, help_text='Deprecated in v3.6 in favor of `role`.') + device_role = NestedDeviceRoleSerializer(read_only=True, help_text=_('Deprecated in v3.6 in favor of `role`.')) tenant = NestedTenantSerializer(required=False, allow_null=True, default=None) platform = NestedPlatformSerializer(required=False, allow_null=True) site = NestedSiteSerializer() diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 47974096f..95e6c4098 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -1373,14 +1373,14 @@ class VirtualDeviceContextImportForm(NetBoxModelImportForm): label=_('Device'), queryset=Device.objects.all(), to_field_name='name', - help_text='Assigned role' + help_text=_('Assigned role') ) tenant = CSVModelChoiceField( label=_('Tenant'), queryset=Tenant.objects.all(), required=False, to_field_name='name', - help_text='Assigned tenant' + help_text=_('Assigned tenant') ) status = CSVChoiceField( label=_('Status'), diff --git a/netbox/netbox/forms/base.py b/netbox/netbox/forms/base.py index 736a2fe9b..1a4155aab 100644 --- a/netbox/netbox/forms/base.py +++ b/netbox/netbox/forms/base.py @@ -78,7 +78,7 @@ class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm): queryset=Tag.objects.all(), required=False, to_field_name='slug', - help_text='Tag slugs separated by commas, encased with double quotes (e.g. "tag1,tag2,tag3")' + help_text=_('Tag slugs separated by commas, encased with double quotes (e.g. "tag1,tag2,tag3")') ) def _get_custom_fields(self, content_type): diff --git a/netbox/wireless/forms/bulk_import.py b/netbox/wireless/forms/bulk_import.py index c0e2dfb54..38bc37360 100644 --- a/netbox/wireless/forms/bulk_import.py +++ b/netbox/wireless/forms/bulk_import.py @@ -42,7 +42,7 @@ class WirelessLANImportForm(NetBoxModelImportForm): status = CSVChoiceField( label=_('Status'), choices=WirelessLANStatusChoices, - help_text='Operational status' + help_text=_('Operational status') ) vlan = CSVModelChoiceField( label=_('VLAN'), From a61e20849b5ec9493e33e64560db5588ef9690ae Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 11:46:03 -0400 Subject: [PATCH 084/303] Release v3.7.6 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- base_requirements.txt | 3 ++- docs/release-notes/version-3.7.md | 2 +- netbox/netbox/settings.py | 2 +- netbox/translations/fr/LC_MESSAGES/django.mo | Bin 220300 -> 220302 bytes netbox/translations/ja/LC_MESSAGES/django.mo | Bin 233614 -> 233577 bytes requirements.txt | 6 +++--- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 60d9a7091..8fc9bc205 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -26,7 +26,7 @@ body: attributes: label: NetBox Version description: What version of NetBox are you currently running? - placeholder: v3.7.5 + placeholder: v3.7.6 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 22c65f276..3e7372484 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.7.5 + placeholder: v3.7.6 validations: required: true - type: dropdown diff --git a/base_requirements.txt b/base_requirements.txt index 642450cf8..8971ebe1e 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -61,7 +61,8 @@ django-timezone-field # A REST API framework for Django projects # https://www.django-rest-framework.org/community/release-notes/ -djangorestframework +# Pinned to 3.14 for NetBox v3.7 +djangorestframework<3.15 # Sane and flexible OpenAPI 3 schema generation for Django REST framework. # https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index e42720eaf..062dc3fe7 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -1,6 +1,6 @@ # NetBox v3.7 -## v3.7.6 (FUTURE) +## v3.7.6 (2024-04-22) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 55002aa87..764aa049a 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -28,7 +28,7 @@ from netbox.plugins import PluginConfig # Environment setup # -VERSION = '3.7.6-dev' +VERSION = '3.7.6' # Hostname HOSTNAME = platform.node() diff --git a/netbox/translations/fr/LC_MESSAGES/django.mo b/netbox/translations/fr/LC_MESSAGES/django.mo index b5dac2ef5cd89f65ae7147f48d886b2a5d232d7f..4bb44f73d388f5c190a23b3bb4df7429987373e2 100644 GIT binary patch delta 23075 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 HtI7WXtFt|y diff --git a/netbox/translations/ja/LC_MESSAGES/django.mo b/netbox/translations/ja/LC_MESSAGES/django.mo index 2962616319d987b8cfaa1e2dd83024464a28c60c..da77c423a93b827c487bdcf2cb38945c431e816c 100644 GIT binary patch delta 23136 zcmXxrb%0gH`^WLay-T_XOLuo84bt7+T}yXL9bggZmZfD`loU}KNoi?B`4SQ?B_fDO zC?dc2=g#x{>oqfH&df7)&bf=uiFyCm)$ec3)z7#3oo9 zXW|l!!dza!`vlixI2MZ+@G4?OOod}HG0wxxxC~R`Zp?*eFgZTM^cV;PeD5WROo4#6 z7E=ZT-ay=qLojbxz-xd@F$vzsw)hN(V*U65Zx9~9c332V3vfCvBp;D5;C+Ul;|Q#o z$d!MKb;vhP><7GBB$gx&cpu^|GgFd)H;;TgV(7A%a3le;M_g9*vk zK}~rROor_+h`Kn7vVYzqRLaE z_CjjZ-pPUKu#}ZILIu{|uJ=RjwF#J-c)TyH!annic>}Xk@iD5w_fxor@}g#|2jTBDA33TqDC~uj74p(@61!E8!w=q zgmI|*`(<<^8ex8n+B=_O5Bw5!Uy4jFpxmgLuIiIeMBP!3#=+PXC!qp~L){q29Po}} zYCMbAP@8pq7PmA%pz?p=UCf#_;Pu9^Y=O|DdjM)6&rtonMlG2iKYPIINFp^h!(kYS z$FT(#&f%U^lTrE8sD`rT40xL`FKXs~KsEd;YDQjKxtA;8jUk@~JL7z8jL(oqyzf=b z?ViosP-~n&kFzFfnPkTh`PTX>iN(P6~Ii?z*b>t+>6@u&#=AD zf7bjiqOqtoUy15yALhbK<|`}DRKRugfmsdJQ8QG-JyA=v(DKXd`bkuP*Rc`)hW=6# z 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 P3KUJO#MbA5n=Aeg7)`GJ delta 23150 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 diff --git a/requirements.txt b/requirements.txt index 2fbc25b21..78b423692 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,11 +18,11 @@ drf-spectacular==0.27.2 drf-spectacular-sidecar==2024.4.1 feedparser==6.0.11 graphene-django==3.0.0 -gunicorn==21.2.0 +gunicorn==22.0.0 Jinja2==3.1.3 Markdown==3.6 -mkdocs-material==9.5.17 -mkdocstrings[python-legacy]==0.24.2 +mkdocs-material==9.5.18 +mkdocstrings[python-legacy]==0.24.3 netaddr==1.2.1 Pillow==10.3.0 psycopg[binary,pool]==3.1.18 From 0b0dab42eb981bfb0d17f9c9164109cf016dd9f0 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 12:23:31 -0400 Subject: [PATCH 085/303] PRVB --- docs/release-notes/version-3.7.md | 4 ++++ netbox/netbox/settings.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index 062dc3fe7..64fdc7dfe 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -1,5 +1,9 @@ # NetBox v3.7 +## v3.7.7 (FUTURE) + +--- + ## v3.7.6 (2024-04-22) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 764aa049a..94fb9f891 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -28,7 +28,7 @@ from netbox.plugins import PluginConfig # Environment setup # -VERSION = '3.7.6' +VERSION = '3.7.7-dev' # Hostname HOSTNAME = platform.node() From e3c418263e0e203af56586b17ba0eb9548be46e0 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 13:38:54 -0400 Subject: [PATCH 086/303] Fixes #15778: Fix bulk edit/delete functionality when HTMX is enabled --- netbox/utilities/templatetags/buttons.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/netbox/utilities/templatetags/buttons.py b/netbox/utilities/templatetags/buttons.py index c4a1086f0..30d55d10c 100644 --- a/netbox/utilities/templatetags/buttons.py +++ b/netbox/utilities/templatetags/buttons.py @@ -152,8 +152,8 @@ def export_button(context, model): } -@register.inclusion_tag('buttons/bulk_edit.html') -def bulk_edit_button(model, action='bulk_edit', query_params=None): +@register.inclusion_tag('buttons/bulk_edit.html', takes_context=True) +def bulk_edit_button(context, model, action='bulk_edit', query_params=None): try: url = reverse(get_viewname(model, action)) if query_params: @@ -162,12 +162,13 @@ def bulk_edit_button(model, action='bulk_edit', query_params=None): url = None return { + 'htmx_navigation': context.get('htmx_navigation'), 'url': url, } -@register.inclusion_tag('buttons/bulk_delete.html') -def bulk_delete_button(model, action='bulk_delete', query_params=None): +@register.inclusion_tag('buttons/bulk_delete.html', takes_context=True) +def bulk_delete_button(context, model, action='bulk_delete', query_params=None): try: url = reverse(get_viewname(model, action)) if query_params: @@ -176,5 +177,6 @@ def bulk_delete_button(model, action='bulk_delete', query_params=None): url = None return { + 'htmx_navigation': context.get('htmx_navigation'), 'url': url, } From c43b9295428e57a47cbca928a52978674feb0cd2 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 14:08:27 -0400 Subject: [PATCH 087/303] Fixes #15580: Fix rendering of modals with HTMX enabled --- netbox/templates/base/layout.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index 04ab9314b..9365df43b 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -126,6 +126,9 @@ Blocks: {% endif %} {# /Bottom banner #} + {# BS5 pop-up modals #} + {% block modals %}{% endblock %} +
    {# Page footer #} @@ -197,8 +200,5 @@ Blocks: {# /Page content #}
    - {# BS5 pop-up modals #} - {% block modals %}{% endblock %} -
    {% endblock layout %} From 8364e632b712a64b46c7eb17adf6ea67d75ac5ca Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 15:08:34 -0400 Subject: [PATCH 088/303] Remove obsolete type definitions --- netbox/project-static/src/global.d.ts | 109 -------------------------- 1 file changed, 109 deletions(-) diff --git a/netbox/project-static/src/global.d.ts b/netbox/project-static/src/global.d.ts index 89c106e9c..d7244339a 100644 --- a/netbox/project-static/src/global.d.ts +++ b/netbox/project-static/src/global.d.ts @@ -68,123 +68,14 @@ type APIObjectBase = { [k: string]: JSONAble; }; -type APIKeyPair = { - public_key: string; - private_key: string; -}; - -type APIReference = { - id: number; - name: string; - slug: string; - url: string; - _depth: number; -}; - -type APISecret = { - assigned_object: APIObjectBase; - assigned_object_id: number; - assigned_object_type: string; - created: string; - custom_fields: Record; - display: string; - hash: string; - id: number; - last_updated: string; - name: string; - plaintext: Nullable; - role: APIObjectBase; - tags: number[]; - url: string; -}; - type APIUserConfig = { tables: { [k: string]: { columns: string[]; available_columns: string[] } }; [k: string]: unknown; }; -type LLDPInterface = { - parent_interface: string | null; - remote_chassis_id: string | null; - remote_port: string | null; - remote_port_description: string | null; - remote_system_capab: string[]; - remote_system_description: string | null; - remote_system_enable_capab: string[]; - remote_system_name: string | null; -}; - -type LLDPNeighborDetail = { - get_lldp_neighbors_detail: { [interface: string]: LLDPInterface[] }; -}; - -type DeviceConfig = { - get_config: { - candidate: string | Record; - running: string | Record; - startup: string | Record; - error?: string; - }; -}; - -type DeviceConfigType = Exclude; - -type DeviceEnvironment = { - cpu?: { - [core: string]: { '%usage': number }; - }; - memory?: { - available_ram: number; - used_ram: number; - }; - power?: { - [psu: string]: { capacity: number; output: number; status: boolean }; - }; - temperature?: { - [sensor: string]: { - is_alert: boolean; - is_critical: boolean; - temperature: number; - }; - }; - fans?: { - [fan: string]: { - status: boolean; - }; - }; -}; - -type DeviceFacts = { - fqdn: string; - hostname: string; - interface_list: string[]; - model: string; - os_version: string; - serial_number: string; - uptime: number; - vendor: string; -}; - -type DeviceStatus = { - get_environment: DeviceEnvironment | ErrorBase; - get_facts: DeviceFacts | ErrorBase; -}; - -interface ObjectWithGroup extends APIObjectBase { - group: Nullable; -} - declare const messages: string[]; type FormControls = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; type ColorMode = 'light' | 'dark'; type ColorModePreference = ColorMode | 'none'; -type ConfigContextFormat = 'json' | 'yaml'; - -type UserPreferences = { - ui: { - colorMode: ColorMode; - showRackImages: boolean; - }; -}; From c32dff56498182065c54683e57c0bbff897f1fbc Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 15:35:34 -0400 Subject: [PATCH 089/303] Release v4.0-beta2 --- docs/release-notes/version-4.0.md | 12 +++++++++++- netbox/netbox/settings.py | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/version-4.0.md b/docs/release-notes/version-4.0.md index 2bdb340ab..8add21fba 100644 --- a/docs/release-notes/version-4.0.md +++ b/docs/release-notes/version-4.0.md @@ -1,6 +1,6 @@ # NetBox v4.0 -## v4.0-beta2 (FUTURE) +## v4.0-beta2 (2024-04-22) **WARNING:** This is a beta release of NetBox intended for testing and evaluation. **Do not use this software in production.** Also be aware that no upgrade path is provided to future releases. @@ -87,10 +87,15 @@ The legacy admin user interface is now disabled by default, and the few remainin * [#15383](https://github.com/netbox-community/netbox/issues/15383) - Standardize filtering logic for the parents of recursively-nested models (parent & ancestor filters) * [#15413](https://github.com/netbox-community/netbox/issues/15413) - The global search engine now supports caching of non-field object attributes * [#15490](https://github.com/netbox-community/netbox/issues/15490) - Custom validators can now reference related object attributes via dotted paths +* [#15547](https://github.com/netbox-community/netbox/issues/15547) - Add comments field to CustomField model +* [#15712](https://github.com/netbox-community/netbox/issues/15712) - Enable image attachments for virtual machines * [#15735](https://github.com/netbox-community/netbox/issues/15735) - Display all dates & times in ISO 8601 format consistently +* [#15754](https://github.com/netbox-community/netbox/issues/15754) - Remove `is_staff` restriction on admin menu items +* [#15764](https://github.com/netbox-community/netbox/issues/15764) - Increase maximum value of Device `vc_position` field ### Bug Fixes (from Beta1) +* [#15580](https://github.com/netbox-community/netbox/issues/15580) - Fix rendering of modals with HTMX navigation enabled * [#15605](https://github.com/netbox-community/netbox/issues/15605) - Fix `ProgrammingError` exception when applying migrations to older databases * [#15613](https://github.com/netbox-community/netbox/issues/15613) - Restore the login button/user menu on mobile view * [#15616](https://github.com/netbox-community/netbox/issues/15616) - Fix button style for invalid custom links @@ -99,9 +104,14 @@ The legacy admin user interface is now disabled by default, and the few remainin * [#15636](https://github.com/netbox-community/netbox/issues/15636) - Fix filtering of attached images when viewing an object in the UI * [#15637](https://github.com/netbox-community/netbox/issues/15637) - Correct nonfunctional links within embedded tables when HTMX enabled * [#15638](https://github.com/netbox-community/netbox/issues/15638) - Correct parameter used to retrieve saved filters for a model +* [#15641](https://github.com/netbox-community/netbox/issues/15641) - Fix adding/removing filters on the advanced object selector widget * [#15652](https://github.com/netbox-community/netbox/issues/15652) - Fix the display of error messages after attempting to delete an object * [#15671](https://github.com/netbox-community/netbox/issues/15671) - Fix `ValueError` exception when uploading a custom script * [#15695](https://github.com/netbox-community/netbox/issues/15695) - Fix `ForeignKeyViolation` exception when applying migration `users.0006_custom_group_model` on older databases +* [#15698](https://github.com/netbox-community/netbox/issues/15698) - Fix ProgrammingError exception when applying the `users.0008_flip_objectpermission_assignments` migration to older databases +* [#15760](https://github.com/netbox-community/netbox/issues/15760) - Permit breaking of long words for wrap within object attribute tables +* [#15778](https://github.com/netbox-community/netbox/issues/15778) - Fix bulk edit/delete functionality when HTMX is enabled +* [#15789](https://github.com/netbox-community/netbox/issues/15789) - Avoid AttributeError exception when attempting to view script results before job execution has completed ### Other Changes diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 30c0fba53..7663f137a 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -24,7 +24,7 @@ from utilities.string import trailing_slash # Environment setup # -VERSION = '4.0-beta1' +VERSION = '4.0-beta2' HOSTNAME = platform.node() # Set the base directory two levels up BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From d606cf1b3c193c5070b36dcbc122bb511bf33f1d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 22 Apr 2024 15:50:38 -0400 Subject: [PATCH 090/303] Update source translations --- netbox/translations/en/LC_MESSAGES/django.po | 399 +++++++++++-------- 1 file changed, 228 insertions(+), 171 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index dfb5a7a59..2a5a12ba7 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-04 19:11+0000\n" +"POT-Creation-Date: 2024-04-22 19:49+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -99,7 +99,7 @@ msgstr "" #: dcim/filtersets.py:903 dcim/filtersets.py:1207 dcim/filtersets.py:1702 #: dcim/filtersets.py:1945 dcim/filtersets.py:2003 ipam/filtersets.py:305 #: ipam/filtersets.py:896 virtualization/filtersets.py:45 -#: virtualization/filtersets.py:173 vpn/filtersets.py:330 +#: virtualization/filtersets.py:173 vpn/filtersets.py:341 msgid "Region (ID)" msgstr "" @@ -109,7 +109,7 @@ msgstr "" #: dcim/filtersets.py:1952 dcim/filtersets.py:2010 extras/filtersets.py:414 #: ipam/filtersets.py:312 ipam/filtersets.py:903 #: virtualization/filtersets.py:52 virtualization/filtersets.py:180 -#: vpn/filtersets.py:325 +#: vpn/filtersets.py:336 msgid "Region (slug)" msgstr "" @@ -182,7 +182,7 @@ msgstr "" #: dcim/filtersets.py:363 extras/filtersets.py:436 ipam/filtersets.py:215 #: ipam/filtersets.py:335 ipam/filtersets.py:926 #: virtualization/filtersets.py:75 virtualization/filtersets.py:203 -#: vpn/filtersets.py:335 +#: vpn/filtersets.py:346 msgid "Site (slug)" msgstr "" @@ -227,7 +227,7 @@ msgstr "" #: dcim/filtersets.py:1232 dcim/filtersets.py:1727 dcim/filtersets.py:1969 #: dcim/filtersets.py:2028 ipam/filtersets.py:209 ipam/filtersets.py:329 #: ipam/filtersets.py:920 virtualization/filtersets.py:69 -#: virtualization/filtersets.py:197 vpn/filtersets.py:340 +#: virtualization/filtersets.py:197 vpn/filtersets.py:351 msgid "Site (ID)" msgstr "" @@ -239,11 +239,11 @@ msgstr "" #: extras/filtersets.py:403 extras/filtersets.py:562 extras/filtersets.py:604 #: extras/filtersets.py:645 ipam/forms/model_forms.py:416 #: netbox/filtersets.py:275 netbox/forms/__init__.py:23 -#: netbox/forms/base.py:163 templates/htmx/object_selector.html:28 +#: netbox/forms/base.py:158 templates/htmx/object_selector.html:28 #: templates/inc/filter_list.html:53 templates/ipam/ipaddress_assign.html:32 #: templates/search.html:7 templates/search.html:26 tenancy/filtersets.py:87 #: users/filtersets.py:21 users/filtersets.py:37 users/filtersets.py:69 -#: users/filtersets.py:117 utilities/forms/forms.py:99 +#: users/filtersets.py:117 utilities/forms/forms.py:105 msgid "Search" msgstr "" @@ -579,7 +579,7 @@ msgstr "" #: circuits/forms/bulk_edit.py:169 circuits/forms/model_forms.py:112 #: dcim/forms/model_forms.py:141 dcim/forms/model_forms.py:183 #: dcim/forms/model_forms.py:260 dcim/forms/model_forms.py:679 -#: dcim/forms/model_forms.py:1485 ipam/forms/model_forms.py:61 +#: dcim/forms/model_forms.py:1574 ipam/forms/model_forms.py:61 #: ipam/forms/model_forms.py:114 ipam/forms/model_forms.py:135 #: ipam/forms/model_forms.py:159 ipam/forms/model_forms.py:231 #: ipam/forms/model_forms.py:257 netbox/navigation/menu.py:38 @@ -620,18 +620,19 @@ msgstr "" #: ipam/forms/bulk_import.py:258 ipam/forms/bulk_import.py:294 #: ipam/forms/bulk_import.py:460 virtualization/forms/bulk_import.py:56 #: virtualization/forms/bulk_import.py:82 vpn/forms/bulk_import.py:39 +#: wireless/forms/bulk_import.py:45 msgid "Operational status" msgstr "" #: circuits/forms/bulk_import.py:104 dcim/forms/bulk_import.py:110 #: dcim/forms/bulk_import.py:155 dcim/forms/bulk_import.py:286 #: dcim/forms/bulk_import.py:428 dcim/forms/bulk_import.py:1171 -#: dcim/forms/bulk_import.py:1319 ipam/forms/bulk_import.py:41 -#: ipam/forms/bulk_import.py:70 ipam/forms/bulk_import.py:98 -#: ipam/forms/bulk_import.py:118 ipam/forms/bulk_import.py:138 -#: ipam/forms/bulk_import.py:167 ipam/forms/bulk_import.py:253 -#: ipam/forms/bulk_import.py:289 ipam/forms/bulk_import.py:455 -#: virtualization/forms/bulk_import.py:70 +#: dcim/forms/bulk_import.py:1319 dcim/forms/bulk_import.py:1383 +#: ipam/forms/bulk_import.py:41 ipam/forms/bulk_import.py:70 +#: ipam/forms/bulk_import.py:98 ipam/forms/bulk_import.py:118 +#: ipam/forms/bulk_import.py:138 ipam/forms/bulk_import.py:167 +#: ipam/forms/bulk_import.py:253 ipam/forms/bulk_import.py:289 +#: ipam/forms/bulk_import.py:455 virtualization/forms/bulk_import.py:70 #: virtualization/forms/bulk_import.py:119 vpn/forms/bulk_import.py:63 #: wireless/forms/bulk_import.py:59 wireless/forms/bulk_import.py:101 msgid "Assigned tenant" @@ -1114,6 +1115,10 @@ msgstr "" msgid "ASN Count" msgstr "" +#: core/api/views.py:39 +msgid "This user does not have permission to synchronize this data source." +msgstr "" + #: core/choices.py:18 msgid "New" msgstr "" @@ -1242,9 +1247,9 @@ msgstr "" msgid "Ignore rules" msgstr "" -#: core/forms/filtersets.py:26 core/forms/model_forms.py:95 -#: extras/forms/model_forms.py:167 extras/forms/model_forms.py:464 -#: extras/forms/model_forms.py:517 extras/tables/tables.py:149 +#: core/forms/filtersets.py:26 core/forms/model_forms.py:96 +#: extras/forms/model_forms.py:167 extras/forms/model_forms.py:465 +#: extras/forms/model_forms.py:518 extras/tables/tables.py:149 #: extras/tables/tables.py:368 extras/tables/tables.py:403 #: templates/core/datasource.html:31 #: templates/dcim/device/render_config.html:19 @@ -1321,89 +1326,89 @@ msgstr "" msgid "User" msgstr "" -#: core/forms/model_forms.py:52 core/tables/data.py:46 +#: core/forms/model_forms.py:53 core/tables/data.py:46 #: templates/core/datafile.html:36 templates/extras/report/base.html:33 #: templates/extras/script/base.html:32 templates/extras/script_result.html:45 msgid "Source" msgstr "" -#: core/forms/model_forms.py:56 +#: core/forms/model_forms.py:57 msgid "Backend Parameters" msgstr "" -#: core/forms/model_forms.py:94 +#: core/forms/model_forms.py:95 msgid "File Upload" msgstr "" -#: core/forms/model_forms.py:106 +#: core/forms/model_forms.py:107 msgid "Cannot upload a file and sync from an existing file" msgstr "" -#: core/forms/model_forms.py:108 +#: core/forms/model_forms.py:109 msgid "Must upload a file or select a data file to sync" msgstr "" -#: core/forms/model_forms.py:147 templates/core/configrevision.html:43 +#: core/forms/model_forms.py:151 templates/core/configrevision.html:43 #: templates/dcim/rack_elevation_list.html:6 msgid "Rack Elevations" msgstr "" -#: core/forms/model_forms.py:148 dcim/choices.py:1413 +#: core/forms/model_forms.py:152 dcim/choices.py:1413 #: dcim/forms/bulk_edit.py:859 dcim/forms/bulk_edit.py:1242 #: dcim/forms/bulk_edit.py:1260 dcim/tables/racks.py:89 #: netbox/navigation/menu.py:276 netbox/navigation/menu.py:280 msgid "Power" msgstr "" -#: core/forms/model_forms.py:149 netbox/navigation/menu.py:142 +#: core/forms/model_forms.py:153 netbox/navigation/menu.py:142 #: templates/core/configrevision.html:79 msgid "IPAM" msgstr "" -#: core/forms/model_forms.py:150 netbox/navigation/menu.py:218 +#: core/forms/model_forms.py:154 netbox/navigation/menu.py:218 #: templates/core/configrevision.html:95 vpn/forms/bulk_edit.py:76 #: vpn/forms/filtersets.py:42 vpn/forms/model_forms.py:60 #: vpn/forms/model_forms.py:145 msgid "Security" msgstr "" -#: core/forms/model_forms.py:151 templates/core/configrevision.html:107 +#: core/forms/model_forms.py:155 templates/core/configrevision.html:107 msgid "Banners" msgstr "" -#: core/forms/model_forms.py:152 templates/core/configrevision.html:131 +#: core/forms/model_forms.py:156 templates/core/configrevision.html:131 msgid "Pagination" msgstr "" -#: core/forms/model_forms.py:153 extras/forms/model_forms.py:63 +#: core/forms/model_forms.py:157 extras/forms/model_forms.py:63 #: templates/core/configrevision.html:147 msgid "Validation" msgstr "" -#: core/forms/model_forms.py:154 templates/account/preferences.html:6 +#: core/forms/model_forms.py:158 templates/account/preferences.html:6 #: templates/core/configrevision.html:175 msgid "User Preferences" msgstr "" -#: core/forms/model_forms.py:155 dcim/forms/filtersets.py:658 +#: core/forms/model_forms.py:159 dcim/forms/filtersets.py:658 #: templates/core/configrevision.html:193 users/forms/model_forms.py:64 msgid "Miscellaneous" msgstr "" -#: core/forms/model_forms.py:158 +#: core/forms/model_forms.py:162 msgid "Config Revision" msgstr "" -#: core/forms/model_forms.py:197 +#: core/forms/model_forms.py:201 msgid "This parameter has been defined statically and cannot be modified." msgstr "" -#: core/forms/model_forms.py:205 +#: core/forms/model_forms.py:209 #, python-brace-format msgid "Current value: {value}" msgstr "" -#: core/forms/model_forms.py:207 +#: core/forms/model_forms.py:211 msgid " (default)" msgstr "" @@ -1640,7 +1645,7 @@ msgstr "" #: core/tables/jobs.py:10 dcim/tables/devicetypes.py:161 #: extras/tables/tables.py:174 extras/tables/tables.py:345 #: netbox/tables/tables.py:184 templates/dcim/virtualchassis_edit.html:53 -#: wireless/tables/wirelesslink.py:16 +#: utilities/forms/forms.py:74 wireless/tables/wirelesslink.py:16 msgid "ID" msgstr "" @@ -1672,6 +1677,10 @@ msgstr "" msgid "Position (U)" msgstr "" +#: dcim/api/serializers.py:671 +msgid "Deprecated in v3.6 in favor of `role`." +msgstr "" + #: dcim/choices.py:21 virtualization/choices.py:21 msgid "Staging" msgstr "" @@ -1748,7 +1757,7 @@ msgstr "" #: dcim/forms/bulk_import.py:778 dcim/forms/bulk_import.py:1033 #: dcim/forms/filtersets.py:226 dcim/forms/model_forms.py:73 #: dcim/forms/model_forms.py:94 dcim/forms/model_forms.py:172 -#: dcim/forms/model_forms.py:962 dcim/forms/model_forms.py:1303 +#: dcim/forms/model_forms.py:962 dcim/forms/model_forms.py:1392 #: dcim/forms/object_import.py:181 dcim/tables/devices.py:680 #: dcim/tables/devices.py:964 extras/tables/tables.py:181 #: ipam/tables/fhrp.py:59 ipam/tables/ip.py:374 ipam/tables/services.py:44 @@ -1859,7 +1868,7 @@ msgstr "" #: dcim/choices.py:796 dcim/choices.py:1022 dcim/forms/bulk_edit.py:1398 #: dcim/forms/filtersets.py:1233 dcim/forms/model_forms.py:888 -#: dcim/forms/model_forms.py:1197 netbox/navigation/menu.py:128 +#: dcim/forms/model_forms.py:1286 netbox/navigation/menu.py:128 #: netbox/navigation/menu.py:132 templates/dcim/interface.html:217 msgid "Wireless" msgstr "" @@ -2270,7 +2279,7 @@ msgstr "" #: dcim/filtersets.py:1172 dcim/filtersets.py:1264 ipam/filtersets.py:577 #: ipam/filtersets.py:807 ipam/filtersets.py:1026 -#: virtualization/filtersets.py:161 vpn/filtersets.py:351 +#: virtualization/filtersets.py:161 vpn/filtersets.py:362 msgid "Device (ID)" msgstr "" @@ -2279,7 +2288,7 @@ msgid "Rack (name)" msgstr "" #: dcim/filtersets.py:1270 ipam/filtersets.py:572 ipam/filtersets.py:802 -#: ipam/filtersets.py:1032 vpn/filtersets.py:346 +#: ipam/filtersets.py:1032 vpn/filtersets.py:357 msgid "Device (name)" msgstr "" @@ -2323,7 +2332,7 @@ msgstr "" #: dcim/filtersets.py:1448 dcim/forms/bulk_edit.py:1374 #: dcim/forms/bulk_import.py:836 dcim/forms/filtersets.py:1328 -#: dcim/forms/model_forms.py:1182 dcim/models/device_components.py:712 +#: dcim/forms/model_forms.py:1271 dcim/models/device_components.py:712 #: dcim/tables/devices.py:646 ipam/filtersets.py:282 ipam/filtersets.py:293 #: ipam/filtersets.py:449 ipam/filtersets.py:550 ipam/filtersets.py:561 #: ipam/forms/bulk_edit.py:226 ipam/forms/bulk_edit.py:281 @@ -2355,7 +2364,7 @@ msgstr "" msgid "VRF (RD)" msgstr "" -#: dcim/filtersets.py:1459 ipam/filtersets.py:967 vpn/filtersets.py:314 +#: dcim/filtersets.py:1459 ipam/filtersets.py:967 vpn/filtersets.py:325 msgid "L2VPN (ID)" msgstr "" @@ -2419,8 +2428,8 @@ msgid "Power panel (ID)" msgstr "" #: dcim/forms/bulk_create.py:40 extras/forms/filtersets.py:410 -#: extras/forms/model_forms.py:453 extras/forms/model_forms.py:504 -#: netbox/forms/base.py:82 netbox/forms/mixins.py:81 +#: extras/forms/model_forms.py:454 extras/forms/model_forms.py:505 +#: netbox/forms/base.py:77 netbox/forms/mixins.py:81 #: netbox/tables/columns.py:448 #: templates/circuits/inc/circuit_termination.html:119 #: templates/generic/bulk_edit.html:81 templates/inc/panels/tags.html:5 @@ -2497,7 +2506,7 @@ msgstr "" #: dcim/forms/bulk_import.py:1021 dcim/forms/filtersets.py:299 #: dcim/forms/filtersets.py:704 dcim/forms/filtersets.py:1417 #: dcim/forms/model_forms.py:224 dcim/forms/model_forms.py:970 -#: dcim/forms/model_forms.py:1311 dcim/forms/object_import.py:186 +#: dcim/forms/model_forms.py:1400 dcim/forms/object_import.py:186 #: dcim/tables/devices.py:202 dcim/tables/devices.py:837 #: dcim/tables/devices.py:948 dcim/tables/devicetypes.py:300 #: dcim/tables/racks.py:69 extras/filtersets.py:457 ipam/forms/bulk_edit.py:245 @@ -2633,8 +2642,9 @@ msgstr "" #: dcim/forms/filtersets.py:247 dcim/forms/filtersets.py:332 #: dcim/forms/filtersets.py:417 dcim/forms/filtersets.py:543 #: dcim/forms/filtersets.py:652 dcim/forms/filtersets.py:853 -#: dcim/forms/model_forms.py:596 dcim/forms/model_forms.py:1381 +#: dcim/forms/model_forms.py:596 dcim/forms/model_forms.py:1470 #: templates/dcim/device_edit.html:20 templates/dcim/inventoryitem_edit.html:23 +#: templates/dcim/inventoryitemtemplate_edit.html:22 msgid "Hardware" msgstr "" @@ -2649,7 +2659,7 @@ msgstr "" #: dcim/forms/filtersets.py:858 dcim/forms/filtersets.py:1423 #: dcim/forms/model_forms.py:274 dcim/forms/model_forms.py:288 #: dcim/forms/model_forms.py:334 dcim/forms/model_forms.py:374 -#: dcim/forms/model_forms.py:975 dcim/forms/model_forms.py:1316 +#: dcim/forms/model_forms.py:975 dcim/forms/model_forms.py:1405 #: dcim/forms/object_import.py:192 dcim/tables/devices.py:129 #: dcim/tables/devices.py:205 dcim/tables/devices.py:951 #: dcim/tables/devicetypes.py:81 dcim/tables/devicetypes.py:304 @@ -2759,8 +2769,8 @@ msgstr "" #: dcim/forms/filtersets.py:1401 dcim/forms/filtersets.py:1412 #: dcim/forms/filtersets.py:1476 dcim/forms/filtersets.py:1500 #: dcim/forms/filtersets.py:1524 dcim/forms/model_forms.py:562 -#: dcim/forms/model_forms.py:760 dcim/forms/model_forms.py:1011 -#: dcim/forms/model_forms.py:1460 dcim/forms/object_create.py:256 +#: dcim/forms/model_forms.py:760 dcim/forms/model_forms.py:1100 +#: dcim/forms/model_forms.py:1549 dcim/forms/object_create.py:256 #: dcim/tables/connections.py:22 dcim/tables/connections.py:41 #: dcim/tables/connections.py:60 dcim/tables/devices.py:318 #: dcim/tables/devices.py:383 dcim/tables/devices.py:427 @@ -2898,8 +2908,8 @@ msgid "Allocated power draw (watts)" msgstr "" #: dcim/forms/bulk_edit.py:968 dcim/forms/bulk_import.py:731 -#: dcim/forms/model_forms.py:855 dcim/forms/model_forms.py:1083 -#: dcim/forms/model_forms.py:1368 dcim/forms/object_import.py:60 +#: dcim/forms/model_forms.py:855 dcim/forms/model_forms.py:1172 +#: dcim/forms/model_forms.py:1457 dcim/forms/object_import.py:60 msgid "Power port" msgstr "" @@ -2932,7 +2942,7 @@ msgid "Wireless role" msgstr "" #: dcim/forms/bulk_edit.py:1178 dcim/forms/model_forms.py:595 -#: dcim/forms/model_forms.py:1026 dcim/tables/devices.py:341 +#: dcim/forms/model_forms.py:1115 dcim/tables/devices.py:341 #: templates/dcim/consoleport.html:27 templates/dcim/consoleserverport.html:27 #: templates/dcim/frontport.html:27 templates/dcim/interface.html:35 #: templates/dcim/module.html:51 templates/dcim/modulebay.html:57 @@ -2946,7 +2956,7 @@ msgstr "" msgid "LAG" msgstr "" -#: dcim/forms/bulk_edit.py:1310 dcim/forms/model_forms.py:1110 +#: dcim/forms/bulk_edit.py:1310 dcim/forms/model_forms.py:1199 msgid "Virtual device contexts" msgstr "" @@ -2970,37 +2980,37 @@ msgstr "" msgid "Mode" msgstr "" -#: dcim/forms/bulk_edit.py:1353 dcim/forms/model_forms.py:1159 +#: dcim/forms/bulk_edit.py:1353 dcim/forms/model_forms.py:1248 #: ipam/forms/bulk_import.py:177 ipam/forms/filtersets.py:479 #: ipam/models/vlans.py:84 virtualization/forms/bulk_edit.py:239 #: virtualization/forms/model_forms.py:324 msgid "VLAN group" msgstr "" -#: dcim/forms/bulk_edit.py:1361 dcim/forms/model_forms.py:1164 +#: dcim/forms/bulk_edit.py:1361 dcim/forms/model_forms.py:1253 #: dcim/tables/devices.py:603 virtualization/forms/bulk_edit.py:247 #: virtualization/forms/model_forms.py:329 msgid "Untagged VLAN" msgstr "" -#: dcim/forms/bulk_edit.py:1369 dcim/forms/model_forms.py:1173 +#: dcim/forms/bulk_edit.py:1369 dcim/forms/model_forms.py:1262 #: dcim/tables/devices.py:609 virtualization/forms/bulk_edit.py:255 #: virtualization/forms/model_forms.py:338 msgid "Tagged VLANs" msgstr "" -#: dcim/forms/bulk_edit.py:1379 dcim/forms/model_forms.py:1146 +#: dcim/forms/bulk_edit.py:1379 dcim/forms/model_forms.py:1235 msgid "Wireless LAN group" msgstr "" -#: dcim/forms/bulk_edit.py:1384 dcim/forms/model_forms.py:1151 +#: dcim/forms/bulk_edit.py:1384 dcim/forms/model_forms.py:1240 #: dcim/tables/devices.py:639 netbox/navigation/menu.py:134 #: templates/dcim/interface.html:289 wireless/tables/wirelesslan.py:24 msgid "Wireless LANs" msgstr "" #: dcim/forms/bulk_edit.py:1393 dcim/forms/filtersets.py:1231 -#: dcim/forms/model_forms.py:1192 ipam/forms/bulk_edit.py:270 +#: dcim/forms/model_forms.py:1281 ipam/forms/bulk_edit.py:270 #: ipam/forms/bulk_edit.py:361 ipam/forms/filtersets.py:166 #: templates/dcim/interface.html:126 templates/ipam/prefix.html:96 #: virtualization/forms/model_forms.py:352 @@ -3008,22 +3018,22 @@ msgid "Addressing" msgstr "" #: dcim/forms/bulk_edit.py:1394 dcim/forms/filtersets.py:651 -#: dcim/forms/model_forms.py:1193 virtualization/forms/model_forms.py:353 +#: dcim/forms/model_forms.py:1282 virtualization/forms/model_forms.py:353 msgid "Operation" msgstr "" #: dcim/forms/bulk_edit.py:1395 dcim/forms/filtersets.py:1232 -#: dcim/forms/model_forms.py:887 dcim/forms/model_forms.py:1195 +#: dcim/forms/model_forms.py:887 dcim/forms/model_forms.py:1284 msgid "PoE" msgstr "" -#: dcim/forms/bulk_edit.py:1396 dcim/forms/model_forms.py:1194 +#: dcim/forms/bulk_edit.py:1396 dcim/forms/model_forms.py:1283 #: templates/dcim/interface.html:101 virtualization/forms/bulk_edit.py:266 #: virtualization/forms/model_forms.py:354 msgid "Related Interfaces" msgstr "" -#: dcim/forms/bulk_edit.py:1397 dcim/forms/model_forms.py:1196 +#: dcim/forms/bulk_edit.py:1397 dcim/forms/model_forms.py:1285 #: virtualization/forms/bulk_edit.py:267 #: virtualization/forms/model_forms.py:355 msgid "802.1Q Switching" @@ -3143,7 +3153,8 @@ msgstr "" msgid "Limit platform assignments to this manufacturer" msgstr "" -#: dcim/forms/bulk_import.py:421 tenancy/forms/bulk_import.py:106 +#: dcim/forms/bulk_import.py:421 dcim/forms/bulk_import.py:1376 +#: tenancy/forms/bulk_import.py:106 msgid "Assigned role" msgstr "" @@ -3272,13 +3283,13 @@ msgstr "" msgid "Electrical phase (for three-phase circuits)" msgstr "" -#: dcim/forms/bulk_import.py:782 dcim/forms/model_forms.py:1121 +#: dcim/forms/bulk_import.py:782 dcim/forms/model_forms.py:1210 #: virtualization/forms/bulk_import.py:155 #: virtualization/forms/model_forms.py:308 msgid "Parent interface" msgstr "" -#: dcim/forms/bulk_import.py:789 dcim/forms/model_forms.py:1129 +#: dcim/forms/bulk_import.py:789 dcim/forms/model_forms.py:1218 #: virtualization/forms/bulk_import.py:162 #: virtualization/forms/model_forms.py:316 msgid "Bridged interface" @@ -3341,7 +3352,7 @@ msgid "VDC {vdc} is not assigned to device {device}" msgstr "" #: dcim/forms/bulk_import.py:896 dcim/forms/model_forms.py:900 -#: dcim/forms/model_forms.py:1376 dcim/forms/object_import.py:122 +#: dcim/forms/model_forms.py:1465 dcim/forms/object_import.py:122 msgid "Rear port" msgstr "" @@ -3572,14 +3583,14 @@ msgstr "" msgid "Connection" msgstr "" -#: dcim/forms/filtersets.py:1245 dcim/forms/model_forms.py:1484 +#: dcim/forms/filtersets.py:1245 dcim/forms/model_forms.py:1573 #: templates/dcim/virtualdevicecontext.html:16 msgid "Virtual Device Context" msgstr "" #: dcim/forms/filtersets.py:1248 extras/forms/bulk_edit.py:315 #: extras/forms/bulk_import.py:245 extras/forms/filtersets.py:479 -#: extras/forms/model_forms.py:557 extras/tables/tables.py:487 +#: extras/forms/model_forms.py:558 extras/tables/tables.py:487 #: templates/extras/journalentry.html:33 msgid "Kind" msgstr "" @@ -3588,7 +3599,7 @@ msgstr "" msgid "Mgmt only" msgstr "" -#: dcim/forms/filtersets.py:1289 dcim/forms/model_forms.py:1187 +#: dcim/forms/filtersets.py:1289 dcim/forms/model_forms.py:1276 #: dcim/models/device_components.py:630 templates/dcim/interface.html:134 msgid "WWN" msgstr "" @@ -3695,18 +3706,52 @@ msgstr "" msgid "Characteristics" msgstr "" -#: dcim/forms/model_forms.py:1137 +#: dcim/forms/model_forms.py:986 +msgid "Console port template" +msgstr "" + +#: dcim/forms/model_forms.py:994 +msgid "Console server port template" +msgstr "" + +#: dcim/forms/model_forms.py:1002 +msgid "Front port template" +msgstr "" + +#: dcim/forms/model_forms.py:1010 +msgid "Interface template" +msgstr "" + +#: dcim/forms/model_forms.py:1018 +msgid "Power outlet template" +msgstr "" + +#: dcim/forms/model_forms.py:1026 +msgid "Power port template" +msgstr "" + +#: dcim/forms/model_forms.py:1034 +msgid "Rear port template" +msgstr "" + +#: dcim/forms/model_forms.py:1087 dcim/forms/model_forms.py:1521 +msgid "An InventoryItem can only be assigned to a single component." +msgstr "" + +#: dcim/forms/model_forms.py:1226 msgid "LAG interface" msgstr "" -#: dcim/forms/model_forms.py:1191 dcim/forms/model_forms.py:1352 +#: dcim/forms/model_forms.py:1280 dcim/forms/model_forms.py:1441 #: dcim/tables/connections.py:65 ipam/forms/bulk_import.py:317 #: ipam/forms/model_forms.py:270 ipam/forms/model_forms.py:279 #: ipam/tables/fhrp.py:64 ipam/tables/ip.py:368 ipam/tables/vlans.py:165 #: templates/circuits/inc/circuit_termination.html:78 #: templates/dcim/frontport.html:113 templates/dcim/interface.html:27 #: templates/dcim/interface.html:190 templates/dcim/interface.html:322 -#: templates/dcim/inventoryitem_edit.html:54 templates/dcim/rearport.html:109 +#: templates/dcim/inventoryitem_edit.html:54 +#: templates/dcim/inventoryitemtemplate_edit.html:51 +#: templates/dcim/rearport.html:109 #: templates/ipam/fhrpgroupassignment_edit.html:11 #: templates/virtualization/vminterface.html:19 #: templates/vpn/tunneltermination.html:32 @@ -3719,52 +3764,49 @@ msgstr "" msgid "Interface" msgstr "" -#: dcim/forms/model_forms.py:1285 +#: dcim/forms/model_forms.py:1374 msgid "Child Device" msgstr "" -#: dcim/forms/model_forms.py:1286 +#: dcim/forms/model_forms.py:1375 msgid "" "Child devices must first be created and assigned to the site and rack of the " "parent device." msgstr "" -#: dcim/forms/model_forms.py:1328 +#: dcim/forms/model_forms.py:1417 msgid "Console port" msgstr "" -#: dcim/forms/model_forms.py:1336 +#: dcim/forms/model_forms.py:1425 msgid "Console server port" msgstr "" -#: dcim/forms/model_forms.py:1344 +#: dcim/forms/model_forms.py:1433 msgid "Front port" msgstr "" -#: dcim/forms/model_forms.py:1360 +#: dcim/forms/model_forms.py:1449 msgid "Power outlet" msgstr "" -#: dcim/forms/model_forms.py:1380 templates/dcim/inventoryitem.html:17 +#: dcim/forms/model_forms.py:1469 templates/dcim/inventoryitem.html:17 #: templates/dcim/inventoryitem_edit.html:10 +#: templates/dcim/inventoryitemtemplate_edit.html:10 msgid "Inventory Item" msgstr "" -#: dcim/forms/model_forms.py:1432 -msgid "An InventoryItem can only be assigned to a single component." -msgstr "" - -#: dcim/forms/model_forms.py:1446 templates/dcim/inventoryitemrole.html:15 +#: dcim/forms/model_forms.py:1535 templates/dcim/inventoryitemrole.html:15 msgid "Inventory Item Role" msgstr "" -#: dcim/forms/model_forms.py:1466 templates/dcim/device.html:195 +#: dcim/forms/model_forms.py:1555 templates/dcim/device.html:195 #: templates/dcim/virtualdevicecontext.html:33 #: templates/virtualization/virtualmachine.html:51 msgid "Primary IPv4" msgstr "" -#: dcim/forms/model_forms.py:1475 templates/dcim/device.html:211 +#: dcim/forms/model_forms.py:1564 templates/dcim/device.html:211 #: templates/dcim/virtualdevicecontext.html:44 #: templates/virtualization/virtualmachine.html:67 msgid "Primary IPv6" @@ -5256,6 +5298,7 @@ msgstr "" #: dcim/tables/connections.py:27 templates/dcim/consoleport.html:18 #: templates/dcim/consoleserverport.html:75 templates/dcim/frontport.html:119 #: templates/dcim/inventoryitem_edit.html:39 +#: templates/dcim/inventoryitemtemplate_edit.html:36 msgid "Console Port" msgstr "" @@ -5266,8 +5309,9 @@ msgid "Reachable" msgstr "" #: dcim/tables/connections.py:46 dcim/tables/devices.py:533 -#: templates/dcim/inventoryitem_edit.html:64 templates/dcim/poweroutlet.html:47 -#: templates/dcim/powerport.html:18 +#: templates/dcim/inventoryitem_edit.html:64 +#: templates/dcim/inventoryitemtemplate_edit.html:61 +#: templates/dcim/poweroutlet.html:47 templates/dcim/powerport.html:18 msgid "Power Port" msgstr "" @@ -5285,7 +5329,7 @@ msgid "VMs" msgstr "" #: dcim/tables/devices.py:133 dcim/tables/devices.py:249 -#: extras/forms/model_forms.py:515 templates/dcim/device.html:114 +#: extras/forms/model_forms.py:516 templates/dcim/device.html:114 #: templates/dcim/device/render_config.html:11 #: templates/dcim/device/render_config.html:15 #: templates/dcim/devicerole.html:47 templates/dcim/platform.html:44 @@ -5350,7 +5394,7 @@ msgstr "" #: dcim/tables/devices.py:279 dcim/tables/devices.py:1091 #: dcim/tables/devicetypes.py:125 dcim/views.py:1005 dcim/views.py:1244 -#: dcim/views.py:1930 netbox/navigation/menu.py:82 +#: dcim/views.py:1932 netbox/navigation/menu.py:82 #: netbox/navigation/menu.py:238 templates/dcim/device/base.html:37 #: templates/dcim/device_list.html:43 templates/dcim/devicetype/base.html:34 #: templates/dcim/module.html:34 templates/dcim/moduletype/base.html:34 @@ -5441,7 +5485,7 @@ msgid "VDCs" msgstr "" #: dcim/tables/devices.py:651 dcim/tables/devicetypes.py:48 -#: dcim/tables/devicetypes.py:140 dcim/views.py:1080 dcim/views.py:2023 +#: dcim/tables/devicetypes.py:140 dcim/views.py:1080 dcim/views.py:2025 #: netbox/navigation/menu.py:91 templates/dcim/device/base.html:52 #: templates/dcim/device_list.html:71 templates/dcim/devicetype/base.html:49 #: templates/dcim/inc/panels/inventory_items.html:5 @@ -5454,6 +5498,7 @@ msgstr "" #: templates/dcim/consoleport.html:81 templates/dcim/consoleserverport.html:81 #: templates/dcim/frontport.html:53 templates/dcim/frontport.html:125 #: templates/dcim/interface.html:196 templates/dcim/inventoryitem_edit.html:69 +#: templates/dcim/inventoryitemtemplate_edit.html:66 #: templates/dcim/rearport.html:18 templates/dcim/rearport.html:115 msgid "Rear Port" msgstr "" @@ -5493,7 +5538,7 @@ msgid "Module Types" msgstr "" #: dcim/tables/devicetypes.py:53 extras/forms/filtersets.py:379 -#: extras/forms/model_forms.py:423 netbox/navigation/menu.py:66 +#: extras/forms/model_forms.py:424 netbox/navigation/menu.py:66 msgid "Platforms" msgstr "" @@ -5514,7 +5559,7 @@ msgid "Instances" msgstr "" #: dcim/tables/devicetypes.py:113 dcim/views.py:945 dcim/views.py:1184 -#: dcim/views.py:1870 netbox/navigation/menu.py:85 +#: dcim/views.py:1872 netbox/navigation/menu.py:85 #: templates/dcim/device/base.html:25 templates/dcim/device_list.html:15 #: templates/dcim/devicetype/base.html:22 templates/dcim/module.html:22 #: templates/dcim/moduletype/base.html:22 @@ -5522,7 +5567,7 @@ msgid "Console Ports" msgstr "" #: dcim/tables/devicetypes.py:116 dcim/views.py:960 dcim/views.py:1199 -#: dcim/views.py:1885 netbox/navigation/menu.py:86 +#: dcim/views.py:1887 netbox/navigation/menu.py:86 #: templates/dcim/device/base.html:28 templates/dcim/device_list.html:22 #: templates/dcim/devicetype/base.html:25 templates/dcim/module.html:25 #: templates/dcim/moduletype/base.html:25 @@ -5530,7 +5575,7 @@ msgid "Console Server Ports" msgstr "" #: dcim/tables/devicetypes.py:119 dcim/views.py:975 dcim/views.py:1214 -#: dcim/views.py:1900 netbox/navigation/menu.py:87 +#: dcim/views.py:1902 netbox/navigation/menu.py:87 #: templates/dcim/device/base.html:31 templates/dcim/device_list.html:29 #: templates/dcim/devicetype/base.html:28 templates/dcim/module.html:28 #: templates/dcim/moduletype/base.html:28 @@ -5538,7 +5583,7 @@ msgid "Power Ports" msgstr "" #: dcim/tables/devicetypes.py:122 dcim/views.py:990 dcim/views.py:1229 -#: dcim/views.py:1915 netbox/navigation/menu.py:88 +#: dcim/views.py:1917 netbox/navigation/menu.py:88 #: templates/dcim/device/base.html:34 templates/dcim/device_list.html:36 #: templates/dcim/devicetype/base.html:31 templates/dcim/module.html:31 #: templates/dcim/moduletype/base.html:31 @@ -5546,27 +5591,27 @@ msgid "Power Outlets" msgstr "" #: dcim/tables/devicetypes.py:128 dcim/views.py:1020 dcim/views.py:1259 -#: dcim/views.py:1951 netbox/navigation/menu.py:83 +#: dcim/views.py:1953 netbox/navigation/menu.py:83 #: templates/dcim/device/base.html:40 templates/dcim/devicetype/base.html:37 #: templates/dcim/module.html:37 templates/dcim/moduletype/base.html:37 msgid "Front Ports" msgstr "" #: dcim/tables/devicetypes.py:131 dcim/views.py:1035 dcim/views.py:1274 -#: dcim/views.py:1966 netbox/navigation/menu.py:84 +#: dcim/views.py:1968 netbox/navigation/menu.py:84 #: templates/dcim/device/base.html:43 templates/dcim/device_list.html:50 #: templates/dcim/devicetype/base.html:40 templates/dcim/module.html:40 #: templates/dcim/moduletype/base.html:40 msgid "Rear Ports" msgstr "" -#: dcim/tables/devicetypes.py:134 dcim/views.py:1065 dcim/views.py:2004 +#: dcim/tables/devicetypes.py:134 dcim/views.py:1065 dcim/views.py:2006 #: netbox/navigation/menu.py:90 templates/dcim/device/base.html:49 #: templates/dcim/device_list.html:57 templates/dcim/devicetype/base.html:46 msgid "Device Bays" msgstr "" -#: dcim/tables/devicetypes.py:137 dcim/views.py:1050 dcim/views.py:1985 +#: dcim/tables/devicetypes.py:137 dcim/views.py:1050 dcim/views.py:1987 #: netbox/navigation/menu.py:89 templates/dcim/device/base.html:46 #: templates/dcim/device_list.html:64 templates/dcim/devicetype/base.html:43 msgid "Module Bays" @@ -5612,7 +5657,7 @@ msgid "Max Weight" msgstr "" #: dcim/tables/sites.py:30 dcim/tables/sites.py:57 -#: extras/forms/filtersets.py:359 extras/forms/model_forms.py:403 +#: extras/forms/filtersets.py:359 extras/forms/model_forms.py:404 #: ipam/forms/bulk_edit.py:128 ipam/forms/model_forms.py:152 #: ipam/tables/asn.py:66 netbox/navigation/menu.py:16 #: netbox/navigation/menu.py:18 @@ -5636,17 +5681,17 @@ msgstr "" msgid "Non-Racked Devices" msgstr "" -#: dcim/views.py:2036 extras/forms/model_forms.py:463 +#: dcim/views.py:2038 extras/forms/model_forms.py:464 #: templates/extras/configcontext.html:10 #: virtualization/forms/model_forms.py:228 virtualization/views.py:408 msgid "Config Context" msgstr "" -#: dcim/views.py:2046 virtualization/views.py:418 +#: dcim/views.py:2048 virtualization/views.py:418 msgid "Render Config" msgstr "" -#: dcim/views.py:2974 ipam/tables/ip.py:233 +#: dcim/views.py:2976 ipam/tables/ip.py:233 msgid "Children" msgstr "" @@ -5890,7 +5935,7 @@ msgid "White" msgstr "" #: extras/choices.py:306 extras/forms/model_forms.py:235 -#: extras/forms/model_forms.py:321 templates/extras/webhook.html:11 +#: extras/forms/model_forms.py:322 templates/extras/webhook.html:11 msgid "Webhook" msgstr "" @@ -6266,7 +6311,7 @@ msgid "Choices" msgstr "" #: extras/forms/filtersets.py:141 extras/forms/filtersets.py:327 -#: extras/forms/filtersets.py:417 extras/forms/model_forms.py:458 +#: extras/forms/filtersets.py:417 extras/forms/model_forms.py:459 #: templates/core/job.html:86 templates/extras/configcontext.html:86 #: templates/extras/eventrule.html:111 msgid "Data" @@ -6286,7 +6331,7 @@ msgstr "" msgid "HTTP content type" msgstr "" -#: extras/forms/filtersets.py:254 extras/forms/model_forms.py:271 +#: extras/forms/filtersets.py:254 extras/forms/model_forms.py:272 #: templates/extras/eventrule.html:46 msgid "Events" msgstr "" @@ -6311,7 +6356,7 @@ msgstr "" msgid "Job starts" msgstr "" -#: extras/forms/filtersets.py:306 extras/forms/model_forms.py:290 +#: extras/forms/filtersets.py:306 extras/forms/model_forms.py:291 msgid "Job terminations" msgstr "" @@ -6323,44 +6368,44 @@ msgstr "" msgid "Allowed object type" msgstr "" -#: extras/forms/filtersets.py:349 extras/forms/model_forms.py:393 +#: extras/forms/filtersets.py:349 extras/forms/model_forms.py:394 #: netbox/navigation/menu.py:19 msgid "Regions" msgstr "" -#: extras/forms/filtersets.py:354 extras/forms/model_forms.py:398 +#: extras/forms/filtersets.py:354 extras/forms/model_forms.py:399 msgid "Site groups" msgstr "" -#: extras/forms/filtersets.py:364 extras/forms/model_forms.py:408 +#: extras/forms/filtersets.py:364 extras/forms/model_forms.py:409 #: netbox/navigation/menu.py:21 msgid "Locations" msgstr "" -#: extras/forms/filtersets.py:369 extras/forms/model_forms.py:413 +#: extras/forms/filtersets.py:369 extras/forms/model_forms.py:414 msgid "Device types" msgstr "" -#: extras/forms/filtersets.py:374 extras/forms/model_forms.py:418 +#: extras/forms/filtersets.py:374 extras/forms/model_forms.py:419 msgid "Roles" msgstr "" -#: extras/forms/filtersets.py:384 extras/forms/model_forms.py:428 +#: extras/forms/filtersets.py:384 extras/forms/model_forms.py:429 msgid "Cluster types" msgstr "" -#: extras/forms/filtersets.py:390 extras/forms/model_forms.py:433 +#: extras/forms/filtersets.py:390 extras/forms/model_forms.py:434 msgid "Cluster groups" msgstr "" -#: extras/forms/filtersets.py:395 extras/forms/model_forms.py:438 +#: extras/forms/filtersets.py:395 extras/forms/model_forms.py:439 #: netbox/navigation/menu.py:243 netbox/navigation/menu.py:245 #: templates/virtualization/clustertype.html:33 #: virtualization/tables/clusters.py:23 virtualization/tables/clusters.py:45 msgid "Clusters" msgstr "" -#: extras/forms/filtersets.py:400 extras/forms/model_forms.py:443 +#: extras/forms/filtersets.py:400 extras/forms/model_forms.py:444 msgid "Tenant groups" msgstr "" @@ -6378,7 +6423,7 @@ msgstr "" msgid "Time" msgstr "" -#: extras/forms/filtersets.py:504 extras/forms/model_forms.py:273 +#: extras/forms/filtersets.py:504 extras/forms/model_forms.py:274 #: extras/tables/tables.py:445 templates/extras/eventrule.html:90 #: templates/extras/objectchange.html:50 msgid "Action" @@ -6439,7 +6484,7 @@ msgid "" "Jinja2 template code for the link URL. Reference the object as {example}." msgstr "" -#: extras/forms/model_forms.py:160 extras/forms/model_forms.py:509 +#: extras/forms/model_forms.py:160 extras/forms/model_forms.py:510 msgid "Template code" msgstr "" @@ -6451,11 +6496,11 @@ msgstr "" msgid "Rendering" msgstr "" -#: extras/forms/model_forms.py:182 extras/forms/model_forms.py:534 +#: extras/forms/model_forms.py:182 extras/forms/model_forms.py:535 msgid "Template content is populated from the remote source selected below." msgstr "" -#: extras/forms/model_forms.py:189 extras/forms/model_forms.py:541 +#: extras/forms/model_forms.py:189 extras/forms/model_forms.py:542 msgid "Must specify either local content or a data file" msgstr "" @@ -6486,55 +6531,55 @@ msgid "" "\">JSON format." msgstr "" -#: extras/forms/model_forms.py:270 templates/extras/eventrule.html:11 +#: extras/forms/model_forms.py:271 templates/extras/eventrule.html:11 msgid "Event Rule" msgstr "" -#: extras/forms/model_forms.py:272 templates/extras/eventrule.html:78 +#: extras/forms/model_forms.py:273 templates/extras/eventrule.html:78 msgid "Conditions" msgstr "" -#: extras/forms/model_forms.py:286 +#: extras/forms/model_forms.py:287 msgid "Creations" msgstr "" -#: extras/forms/model_forms.py:287 +#: extras/forms/model_forms.py:288 msgid "Updates" msgstr "" -#: extras/forms/model_forms.py:288 +#: extras/forms/model_forms.py:289 msgid "Deletions" msgstr "" -#: extras/forms/model_forms.py:289 +#: extras/forms/model_forms.py:290 msgid "Job executions" msgstr "" -#: extras/forms/model_forms.py:375 users/forms/model_forms.py:286 +#: extras/forms/model_forms.py:376 users/forms/model_forms.py:286 msgid "Object types" msgstr "" -#: extras/forms/model_forms.py:448 netbox/navigation/menu.py:40 +#: extras/forms/model_forms.py:449 netbox/navigation/menu.py:40 #: tenancy/tables/tenants.py:22 msgid "Tenants" msgstr "" -#: extras/forms/model_forms.py:465 ipam/forms/filtersets.py:141 +#: extras/forms/model_forms.py:466 ipam/forms/filtersets.py:141 #: ipam/forms/filtersets.py:527 templates/extras/configcontext.html:62 #: templates/ipam/ipaddress.html:62 templates/ipam/vlan_edit.html:30 #: tenancy/forms/filtersets.py:86 users/forms/model_forms.py:324 msgid "Assignment" msgstr "" -#: extras/forms/model_forms.py:491 +#: extras/forms/model_forms.py:492 msgid "Data is populated from the remote source selected below." msgstr "" -#: extras/forms/model_forms.py:497 +#: extras/forms/model_forms.py:498 msgid "Must specify either local data or a data file" msgstr "" -#: extras/forms/model_forms.py:516 templates/core/datafile.html:65 +#: extras/forms/model_forms.py:517 templates/core/datafile.html:65 msgid "Content" msgstr "" @@ -7545,19 +7590,19 @@ msgstr "" msgid "Invalid IP address format: {address}" msgstr "" -#: ipam/filtersets.py:47 vpn/filtersets.py:276 +#: ipam/filtersets.py:47 vpn/filtersets.py:287 msgid "Import target" msgstr "" -#: ipam/filtersets.py:53 vpn/filtersets.py:282 +#: ipam/filtersets.py:53 vpn/filtersets.py:293 msgid "Import target (name)" msgstr "" -#: ipam/filtersets.py:58 vpn/filtersets.py:287 +#: ipam/filtersets.py:58 vpn/filtersets.py:298 msgid "Export target" msgstr "" -#: ipam/filtersets.py:64 vpn/filtersets.py:293 +#: ipam/filtersets.py:64 vpn/filtersets.py:304 msgid "Export target (name)" msgstr "" @@ -7607,11 +7652,11 @@ msgstr "" msgid "Mask length" msgstr "" -#: ipam/filtersets.py:339 vpn/filtersets.py:399 +#: ipam/filtersets.py:339 vpn/filtersets.py:410 msgid "VLAN (ID)" msgstr "" -#: ipam/filtersets.py:343 vpn/filtersets.py:394 +#: ipam/filtersets.py:343 vpn/filtersets.py:405 msgid "VLAN number (1-4094)" msgstr "" @@ -7630,25 +7675,25 @@ msgid "Parent prefix" msgstr "" #: ipam/filtersets.py:582 ipam/filtersets.py:812 ipam/filtersets.py:1042 -#: vpn/filtersets.py:357 +#: vpn/filtersets.py:368 msgid "Virtual machine (name)" msgstr "" #: ipam/filtersets.py:587 ipam/filtersets.py:817 ipam/filtersets.py:1036 #: virtualization/filtersets.py:278 virtualization/filtersets.py:317 -#: vpn/filtersets.py:362 +#: vpn/filtersets.py:373 msgid "Virtual machine (ID)" msgstr "" -#: ipam/filtersets.py:593 vpn/filtersets.py:97 vpn/filtersets.py:368 +#: ipam/filtersets.py:593 vpn/filtersets.py:97 vpn/filtersets.py:379 msgid "Interface (name)" msgstr "" -#: ipam/filtersets.py:598 vpn/filtersets.py:102 vpn/filtersets.py:373 +#: ipam/filtersets.py:598 vpn/filtersets.py:102 vpn/filtersets.py:384 msgid "Interface (ID)" msgstr "" -#: ipam/filtersets.py:604 vpn/filtersets.py:108 vpn/filtersets.py:379 +#: ipam/filtersets.py:604 vpn/filtersets.py:108 vpn/filtersets.py:390 msgid "VM interface (name)" msgstr "" @@ -8912,15 +8957,17 @@ msgstr "" msgid "Object type(s)" msgstr "" -#: netbox/forms/base.py:77 -msgid "Id" +#: netbox/forms/base.py:81 +msgid "" +"Tag slugs separated by commas, encased with double quotes (e.g. \"tag1,tag2," +"tag3\")" msgstr "" -#: netbox/forms/base.py:116 +#: netbox/forms/base.py:111 msgid "Add tags" msgstr "" -#: netbox/forms/base.py:121 +#: netbox/forms/base.py:116 msgid "Remove tags" msgstr "" @@ -9192,8 +9239,9 @@ msgstr "" #: netbox/navigation/menu.py:310 #: templates/circuits/circuittermination_edit.html:53 #: templates/dcim/cable_edit.html:77 templates/dcim/device_edit.html:103 -#: templates/dcim/inventoryitem_edit.html:102 templates/dcim/rack_edit.html:81 -#: templates/dcim/virtualchassis_add.html:31 +#: templates/dcim/inventoryitem_edit.html:102 +#: templates/dcim/inventoryitemtemplate_edit.html:99 +#: templates/dcim/rack_edit.html:81 templates/dcim/virtualchassis_add.html:31 #: templates/dcim/virtualchassis_edit.html:41 #: templates/generic/bulk_edit.html:92 templates/htmx/form.html:32 #: templates/inc/panels/custom_fields.html:7 @@ -9420,31 +9468,31 @@ msgstr "" msgid "Cannot delete stores from registry" msgstr "" -#: netbox/settings.py:724 +#: netbox/settings.py:727 msgid "English" msgstr "" -#: netbox/settings.py:725 +#: netbox/settings.py:728 msgid "Spanish" msgstr "" -#: netbox/settings.py:726 +#: netbox/settings.py:729 msgid "French" msgstr "" -#: netbox/settings.py:727 +#: netbox/settings.py:730 msgid "Japanese" msgstr "" -#: netbox/settings.py:728 +#: netbox/settings.py:731 msgid "Portuguese" msgstr "" -#: netbox/settings.py:729 +#: netbox/settings.py:732 msgid "Russian" msgstr "" -#: netbox/settings.py:730 +#: netbox/settings.py:733 msgid "Turkish" msgstr "" @@ -9904,6 +9952,7 @@ msgstr "" #: templates/dcim/consoleport.html:78 templates/dcim/consoleserverport.html:78 #: templates/dcim/frontport.html:18 templates/dcim/frontport.html:122 #: templates/dcim/interface.html:193 templates/dcim/inventoryitem_edit.html:49 +#: templates/dcim/inventoryitemtemplate_edit.html:46 #: templates/dcim/rearport.html:112 msgid "Front Port" msgstr "" @@ -10144,6 +10193,7 @@ msgstr "" #: templates/dcim/consoleport.html:75 templates/dcim/consoleserverport.html:18 #: templates/dcim/frontport.html:116 templates/dcim/inventoryitem_edit.html:44 +#: templates/dcim/inventoryitemtemplate_edit.html:41 msgid "Console Server Port" msgstr "" @@ -10572,11 +10622,13 @@ msgid "This will also delete all child inventory items of those listed" msgstr "" #: templates/dcim/inventoryitem_edit.html:33 +#: templates/dcim/inventoryitemtemplate_edit.html:30 msgid "Component Assignment" msgstr "" -#: templates/dcim/inventoryitem_edit.html:59 templates/dcim/poweroutlet.html:18 -#: templates/dcim/powerport.html:81 +#: templates/dcim/inventoryitem_edit.html:59 +#: templates/dcim/inventoryitemtemplate_edit.html:56 +#: templates/dcim/poweroutlet.html:18 templates/dcim/powerport.html:81 msgid "Power Outlet" msgstr "" @@ -12841,16 +12893,21 @@ msgstr "" msgid "Use regular expressions" msgstr "" -#: utilities/forms/forms.py:87 +#: utilities/forms/forms.py:76 +msgid "" +"Numeric ID of an existing object to update (if not creating a new object)" +msgstr "" + +#: utilities/forms/forms.py:93 #, python-brace-format msgid "Unrecognized header: {name}" msgstr "" -#: utilities/forms/forms.py:113 +#: utilities/forms/forms.py:119 msgid "Available Columns" msgstr "" -#: utilities/forms/forms.py:121 +#: utilities/forms/forms.py:127 msgid "Selected Columns" msgstr "" @@ -13020,7 +13077,7 @@ msgstr "" msgid "Testing" msgstr "" -#: utilities/testing/views.py:625 +#: utilities/testing/views.py:632 msgid "The test must define csv_update_data." msgstr "" @@ -13377,31 +13434,31 @@ msgstr "" msgid "Outside IP (ID)" msgstr "" -#: vpn/filtersets.py:235 +#: vpn/filtersets.py:142 vpn/filtersets.py:246 msgid "IKE policy (ID)" msgstr "" -#: vpn/filtersets.py:241 +#: vpn/filtersets.py:148 vpn/filtersets.py:252 msgid "IKE policy (name)" msgstr "" -#: vpn/filtersets.py:245 +#: vpn/filtersets.py:256 msgid "IPSec policy (ID)" msgstr "" -#: vpn/filtersets.py:251 +#: vpn/filtersets.py:262 msgid "IPSec policy (name)" msgstr "" -#: vpn/filtersets.py:320 +#: vpn/filtersets.py:331 msgid "L2VPN (slug)" msgstr "" -#: vpn/filtersets.py:384 +#: vpn/filtersets.py:395 msgid "VM Interface (ID)" msgstr "" -#: vpn/filtersets.py:390 +#: vpn/filtersets.py:401 msgid "VLAN (name)" msgstr "" From 6f36b8513c8f3db4adb3dd99bc50e2a39ef3b3d0 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Mon, 22 Apr 2024 21:51:08 -0500 Subject: [PATCH 091/303] Update changelog for #13874 --- docs/release-notes/version-3.7.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index 64fdc7dfe..981d3c3c0 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -2,6 +2,11 @@ ## v3.7.7 (FUTURE) +### Bug Fixes + +* [#13712](https://github.com/netbox-community/netbox/issues/13712) - Fix row highlighting for device interface list display +* [#13806](https://github.com/netbox-community/netbox/issues/13806) - Fix "mark" button tooltip on swap for device interface list display + --- ## v3.7.6 (2024-04-22) From 7b1b91b8eec4bbfb505b56df4d197c2ab80d978d Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Mon, 22 Apr 2024 21:51:54 -0500 Subject: [PATCH 092/303] Correct wording for #13874 --- docs/release-notes/version-3.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index 981d3c3c0..4e72acbd4 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -5,7 +5,7 @@ ### Bug Fixes * [#13712](https://github.com/netbox-community/netbox/issues/13712) - Fix row highlighting for device interface list display -* [#13806](https://github.com/netbox-community/netbox/issues/13806) - Fix "mark" button tooltip on swap for device interface list display +* [#13806](https://github.com/netbox-community/netbox/issues/13806) - Fix "mark" button tooltip on button activation for device interface list display --- From 85db007ff585370b03b2d88d8f43e6fa9b94bc8e Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Mon, 22 Apr 2024 21:57:40 -0500 Subject: [PATCH 093/303] Update changelog for #14750 --- docs/release-notes/version-3.7.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index 4e72acbd4..31b99fcdc 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -6,6 +6,8 @@ * [#13712](https://github.com/netbox-community/netbox/issues/13712) - Fix row highlighting for device interface list display * [#13806](https://github.com/netbox-community/netbox/issues/13806) - Fix "mark" button tooltip on button activation for device interface list display +* [#13922](https://github.com/netbox-community/netbox/issues/13922) - Fix SVG drawing error on multiple termination trace with multiple devices +* [#14241](https://github.com/netbox-community/netbox/issues/14241) - Fix random interface swap when performing cable trace with multiple termination --- From e05ca710ae47db49f8552a77b996ab4f69abaedc Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 23 Apr 2024 10:38:49 -0400 Subject: [PATCH 094/303] Flag HTMX navigation as an experimental feature --- netbox/netbox/preferences.py | 13 +++++-- netbox/templates/account/preferences.html | 46 ++++++++++++----------- netbox/users/forms/model_forms.py | 11 +++++- netbox/users/preferences.py | 3 +- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/netbox/netbox/preferences.py b/netbox/netbox/preferences.py index 1414ea850..aa37bffae 100644 --- a/netbox/netbox/preferences.py +++ b/netbox/netbox/preferences.py @@ -22,6 +22,7 @@ PREFERENCES = { ('dark', _('Dark')), ), default='light', + description=_('Preferred default UI theme') ), 'ui.htmx_navigation': UserPreference( label=_('HTMX Navigation'), @@ -29,14 +30,17 @@ PREFERENCES = { ('', _('Disabled')), ('true', _('Enabled')), ), - default=False + description=_('Enable dynamic UI navigation'), + default=False, + experimental=True ), 'locale.language': UserPreference( label=_('Language'), choices=( ('', _('Auto')), *settings.LANGUAGES, - ) + ), + description=_('Forces UI translation to the specified language.') ), 'pagination.per_page': UserPreference( label=_('Page length'), @@ -51,8 +55,8 @@ PREFERENCES = { ('top', _('Top')), ('both', _('Both')), ), - description=_('Where the paginator controls will be displayed relative to a table'), - default='bottom' + default='bottom', + description=_('Where the paginator controls will be displayed relative to a table') ), # Miscellaneous @@ -62,6 +66,7 @@ PREFERENCES = { ('json', 'JSON'), ('yaml', 'YAML'), ), + description=_('The preferred syntax for displaying generic data within the UI') ), } diff --git a/netbox/templates/account/preferences.html b/netbox/templates/account/preferences.html index 5f63c328e..0276b9adb 100644 --- a/netbox/templates/account/preferences.html +++ b/netbox/templates/account/preferences.html @@ -39,30 +39,32 @@ {% trans "Clear table preferences" %}
    - - - - - - - - - - - {% for table, prefs in request.user.config.data.tables.items %} +
    +
    - - {% trans "Table" %}{% trans "Ordering" %}{% trans "Columns" %}
    + - - - - + + + + - {% endfor %} - -
    - - {{ table }}{{ prefs.ordering|join:", "|placeholder }}{{ prefs.columns|join:", "|placeholder }} + + {% trans "Table" %}{% trans "Ordering" %}{% trans "Columns" %}
    + + + {% for table, prefs in request.user.config.data.tables.items %} + + + + + {{ table }} + {{ prefs.ordering|join:", "|placeholder }} + {{ prefs.columns|join:", "|placeholder }} + + {% endfor %} + + +
    {% else %}
    diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py index 7320e603b..b14300d80 100644 --- a/netbox/users/forms/model_forms.py +++ b/netbox/users/forms/model_forms.py @@ -3,7 +3,7 @@ from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.postgres.forms import SimpleArrayField from django.core.exceptions import FieldError -from django.utils.html import mark_safe +from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ from core.models import ObjectType @@ -37,7 +37,14 @@ class UserConfigFormMetaclass(forms.models.ModelFormMetaclass): preference_fields = {} for field_name, preference in PREFERENCES.items(): description = f'{preference.description}
    ' if preference.description else '' - help_text = f'{description}{field_name}' + help_text = f'{field_name}' + if preference.description: + help_text = f'{preference.description}
    {help_text}' + if preference.experimental: + help_text = ( + f' Experimental feature
    ' + f'{help_text}' + ) field_kwargs = { 'label': preference.label, 'choices': preference.choices, diff --git a/netbox/users/preferences.py b/netbox/users/preferences.py index cff6a3c9b..3eab4cb6e 100644 --- a/netbox/users/preferences.py +++ b/netbox/users/preferences.py @@ -2,9 +2,10 @@ class UserPreference: """ Represents a configurable user preference. """ - def __init__(self, label, choices, default=None, description='', coerce=lambda x: x): + def __init__(self, label, choices, default=None, description='', coerce=lambda x: x, experimental=False): self.label = label self.choices = choices self.default = default if default is not None else choices[0] self.description = description self.coerce = coerce + self.experimental = experimental From ded2fe9471b8a0c03a3bba3a2cf13533ca1c40dc Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Thu, 25 Apr 2024 06:19:19 -0700 Subject: [PATCH 095/303] 15809 Mark unions as nullable in GraphQL where appropriate (#15824) * 15809 mark unions as nullable where appropriate * 15809 fix tests * 15809 fix tests --- netbox/dcim/graphql/types.py | 6 +++--- netbox/ipam/graphql/types.py | 4 ++-- netbox/utilities/testing/api.py | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 023f72be3..99a9106cb 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -130,7 +130,7 @@ class CableTerminationType(NetBoxObjectType): Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')], Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')], Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')], - ], strawberry.union("CableTerminationTerminationType")] + ], strawberry.union("CableTerminationTerminationType")] | None @strawberry_django.type( @@ -302,7 +302,7 @@ class InventoryItemTemplateType(ComponentTemplateType): Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')], Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')], Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')], - ], strawberry.union("InventoryItemTemplateComponentType")] + ], strawberry.union("InventoryItemTemplateComponentType")] | None @strawberry_django.type( @@ -431,7 +431,7 @@ class InventoryItemType(ComponentType): Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')], Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')], Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')], - ], strawberry.union("InventoryItemComponentType")] + ], strawberry.union("InventoryItemComponentType")] | None @strawberry_django.type( diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index 6c269721e..36e09eaac 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -133,7 +133,7 @@ class IPAddressType(NetBoxObjectType, BaseIPAddressFamilyType): Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')], Annotated["FHRPGroupType", strawberry.lazy('ipam.graphql.types')], Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')], - ], strawberry.union("IPAddressAssignmentType")]: + ], strawberry.union("IPAddressAssignmentType")] | None: return self.assigned_object @@ -261,7 +261,7 @@ class VLANGroupType(OrganizationalObjectType): Annotated["RegionType", strawberry.lazy('dcim.graphql.types')], Annotated["SiteType", strawberry.lazy('dcim.graphql.types')], Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')], - ], strawberry.union("VLANGroupScopeType")]: + ], strawberry.union("VLANGroupScopeType")] | None: return self.scope diff --git a/netbox/utilities/testing/api.py b/netbox/utilities/testing/api.py index a30235d93..4802c3ffe 100644 --- a/netbox/utilities/testing/api.py +++ b/netbox/utilities/testing/api.py @@ -469,6 +469,9 @@ class APIViewTestCases: elif type(field.type) is StrawberryUnion: # this would require a fragment query continue + elif type(field.type) is StrawberryOptional and type(field.type.of_type) is StrawberryUnion: + # this would require a fragment query + continue elif type(field.type) is StrawberryOptional and type(field.type.of_type) is LazyType: fields_string += f'{field.name} {{ id }}\n' elif hasattr(field, 'is_relation') and field.is_relation: From 4923025fec2ad1b0fb5d31895a2a101601818ce5 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Thu, 25 Apr 2024 06:22:32 -0700 Subject: [PATCH 096/303] 15541 Add component selector to InventoryItemTemplate (#15691) * 15541 update InventoryItemTemplateForm * 15541 update InventoryItemTemplateForm * Remove custom template --------- Co-authored-by: Jeremy Stretch --- netbox/dcim/forms/model_forms.py | 19 +++- netbox/dcim/views.py | 2 - .../dcim/inventoryitemtemplate_edit.html | 104 ------------------ 3 files changed, 13 insertions(+), 112 deletions(-) delete mode 100644 netbox/templates/dcim/inventoryitemtemplate_edit.html diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 511e0039e..81c1b17c7 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1002,6 +1002,7 @@ class InventoryItemTemplateForm(ComponentTemplateForm): queryset=Manufacturer.objects.all(), required=False ) + # Assigned component selectors consoleporttemplate = DynamicModelChoiceField( queryset=ConsolePortTemplate.objects.all(), @@ -1063,8 +1064,19 @@ class InventoryItemTemplateForm(ComponentTemplateForm): fieldsets = ( FieldSet( 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'description', - 'component_type', 'component_id', ), + FieldSet( + TabbedGroups( + FieldSet('interfacetemplate', name=_('Interface')), + FieldSet('consoleporttemplate', name=_('Console Port')), + FieldSet('consoleserverporttemplate', name=_('Console Server Port')), + FieldSet('frontporttemplate', name=_('Front Port')), + FieldSet('rearporttemplate', name=_('Rear Port')), + FieldSet('powerporttemplate', name=_('Power Port')), + FieldSet('poweroutlettemplate', name=_('Power Outlet')), + ), + name=_('Component Assignment') + ) ) class Meta: @@ -1079,22 +1091,17 @@ class InventoryItemTemplateForm(ComponentTemplateForm): component_type = initial.get('component_type') component_id = initial.get('component_id') - # Used for picking the default active tab for component selection - self.no_component = True - if instance: # When editing set the initial value for component selection for component_model in ContentType.objects.filter(MODULAR_COMPONENT_TEMPLATE_MODELS): if type(instance.component) is component_model.model_class(): initial[component_model.model] = instance.component - self.no_component = False break elif component_type and component_id: # When adding the InventoryItem from a component page if content_type := ContentType.objects.filter(MODULAR_COMPONENT_TEMPLATE_MODELS).filter(pk=component_type).first(): if component := content_type.model_class().objects.filter(pk=component_id).first(): initial[content_type.model] = component - self.no_component = False kwargs['initial'] = initial diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 8968f8c52..120bbcb59 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1655,7 +1655,6 @@ class InventoryItemTemplateCreateView(generic.ComponentCreateView): queryset = InventoryItemTemplate.objects.all() form = forms.InventoryItemTemplateCreateForm model_form = forms.InventoryItemTemplateForm - template_name = 'dcim/inventoryitemtemplate_edit.html' def alter_object(self, instance, request): # Set component (if any) @@ -1673,7 +1672,6 @@ class InventoryItemTemplateCreateView(generic.ComponentCreateView): class InventoryItemTemplateEditView(generic.ObjectEditView): queryset = InventoryItemTemplate.objects.all() form = forms.InventoryItemTemplateForm - template_name = 'dcim/inventoryitemtemplate_edit.html' @register_model_view(InventoryItemTemplate, 'delete') diff --git a/netbox/templates/dcim/inventoryitemtemplate_edit.html b/netbox/templates/dcim/inventoryitemtemplate_edit.html deleted file mode 100644 index d3ac58e25..000000000 --- a/netbox/templates/dcim/inventoryitemtemplate_edit.html +++ /dev/null @@ -1,104 +0,0 @@ -{% extends 'generic/object_edit.html' %} -{% load static %} -{% load form_helpers %} -{% load helpers %} -{% load i18n %} - -{% block form %} -
    -
    -
    {% trans "Inventory Item" %}
    -
    - {% render_field form.device_type %} - {% render_field form.parent %} - {% render_field form.name %} - {% render_field form.label %} - {% render_field form.role %} - {% render_field form.description %} -
    - -
    -
    -
    {% trans "Hardware" %}
    -
    - {% render_field form.manufacturer %} - {% render_field form.part_id %} -
    - -
    -
    -
    {% trans "Component Assignment" %}
    -
    -
    - -
    -
    -
    - {% render_field form.consoleporttemplate %} -
    -
    - {% render_field form.consoleserverporttemplate %} -
    -
    - {% render_field form.frontporttemplate %} -
    -
    - {% render_field form.interfacetemplate %} -
    -
    - {% render_field form.poweroutlettemplate %} -
    -
    - {% render_field form.powerporttemplate %} -
    -
    - {% render_field form.rearporttemplate %} -
    -
    -
    - - {% if form.custom_fields %} -
    -
    -
    {% trans "Custom Fields" %}
    -
    - {% render_custom_fields form %} -
    - {% endif %} -{% endblock %} From 5af3c659a5ea1399e3dbb171137baf26a499abd4 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Wed, 24 Apr 2024 07:27:57 +0200 Subject: [PATCH 097/303] Fix #15826: Added new group and user models --- netbox/netbox/settings.py | 4 ++-- netbox/utilities/testing/views.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 7663f137a..d5cded728 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -477,11 +477,11 @@ SERIALIZATION_MODULES = { # Exclude potentially sensitive models from wildcard view exemption. These may still be exempted # by specifying the model individually in the EXEMPT_VIEW_PERMISSIONS configuration parameter. EXEMPT_EXCLUDE_MODELS = ( - ('auth', 'group'), - ('auth', 'user'), ('extras', 'configrevision'), + ('users', 'group'), ('users', 'objectpermission'), ('users', 'token'), + ('users', 'user'), ) # All URLs starting with a string listed here are exempt from login enforcement diff --git a/netbox/utilities/testing/views.py b/netbox/utilities/testing/views.py index 54e190026..e3b12b4c3 100644 --- a/netbox/utilities/testing/views.py +++ b/netbox/utilities/testing/views.py @@ -151,7 +151,7 @@ class ViewTestCases: with disable_warnings('django.request'): self.assertHttpStatus(response, 403) - @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) def test_create_object_with_permission(self): # Assign unconstrained permission @@ -190,7 +190,7 @@ class ViewTestCases: self.assertEqual(len(objectchanges), 1) self.assertEqual(objectchanges[0].action, ObjectChangeActionChoices.ACTION_CREATE) - @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) def test_create_object_with_constrained_permission(self): # Assign constrained permission @@ -253,7 +253,7 @@ class ViewTestCases: with disable_warnings('django.request'): self.assertHttpStatus(self.client.post(**request), 403) - @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) def test_edit_object_with_permission(self): instance = self._get_queryset().first() @@ -291,7 +291,7 @@ class ViewTestCases: self.assertEqual(len(objectchanges), 1) self.assertEqual(objectchanges[0].action, ObjectChangeActionChoices.ACTION_UPDATE) - @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) def test_edit_object_with_constrained_permission(self): instance1, instance2 = self._get_queryset().all()[:2] @@ -602,7 +602,7 @@ class ViewTestCases: with disable_warnings('django.request'): self.assertHttpStatus(response, 403) - @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) def test_bulk_import_objects_with_permission(self): initial_count = self._get_queryset().count() data = { @@ -665,7 +665,7 @@ class ViewTestCases: if value is not None and not isinstance(field, ForeignKey): self.assertEqual(value, value) - @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) def test_bulk_import_objects_with_constrained_permission(self): initial_count = self._get_queryset().count() data = { @@ -720,7 +720,7 @@ class ViewTestCases: with disable_warnings('django.request'): self.assertHttpStatus(self.client.post(self._get_url('bulk_edit'), data), 403) - @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) def test_bulk_edit_objects_with_permission(self): pk_list = list(self._get_queryset().values_list('pk', flat=True)[:3]) data = { @@ -745,7 +745,7 @@ class ViewTestCases: for i, instance in enumerate(self._get_queryset().filter(pk__in=pk_list)): self.assertInstanceEqual(instance, self.bulk_edit_data) - @override_settings(EXEMPT_VIEW_PERMISSIONS=['*']) + @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[]) def test_bulk_edit_objects_with_constrained_permission(self): pk_list = list(self._get_queryset().values_list('pk', flat=True)[:3]) data = { From 835012f2ed6e2165078fe2ab65314897a71a8240 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 26 Apr 2024 10:13:08 -0700 Subject: [PATCH 098/303] 15838 use naturalday for date not naturaltime --- netbox/utilities/templatetags/builtins/filters.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/netbox/utilities/templatetags/builtins/filters.py b/netbox/utilities/templatetags/builtins/filters.py index 842f7b14a..77db007cc 100644 --- a/netbox/utilities/templatetags/builtins/filters.py +++ b/netbox/utilities/templatetags/builtins/filters.py @@ -5,7 +5,7 @@ import re import yaml from django import template from django.contrib.contenttypes.models import ContentType -from django.contrib.humanize.templatetags.humanize import naturaltime +from django.contrib.humanize.templatetags.humanize import naturalday, naturaltime from django.utils.html import escape from django.utils.safestring import mark_safe from markdown import markdown @@ -216,11 +216,12 @@ def render_yaml(value): def isodate(value): if type(value) is datetime.date: text = value.isoformat() + return mark_safe(f'{text}') elif type(value) is datetime.datetime: text = value.date().isoformat() + return mark_safe(f'{text}') else: return '' - return mark_safe(f'{text}') @register.filter() From 851b4cc4d3f49358b48c1d1e7b6bc2a51aa84473 Mon Sep 17 00:00:00 2001 From: Mattias Loverot Date: Mon, 29 Apr 2024 16:33:05 +0200 Subject: [PATCH 099/303] Added assigned_object_type in prefetch for api view IPAddressViewSet - fixes #15845 --- netbox/ipam/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/ipam/api/views.py b/netbox/ipam/api/views.py index 62e2b9eca..c5a9331ab 100644 --- a/netbox/ipam/api/views.py +++ b/netbox/ipam/api/views.py @@ -119,7 +119,7 @@ class IPRangeViewSet(NetBoxModelViewSet): class IPAddressViewSet(NetBoxModelViewSet): queryset = IPAddress.objects.prefetch_related( - 'vrf__tenant', 'tenant', 'nat_inside', 'nat_outside', 'tags', 'assigned_object' + 'vrf__tenant', 'tenant', 'nat_inside', 'nat_outside', 'tags', 'assigned_object', 'assigned_object_type' ) serializer_class = serializers.IPAddressSerializer filterset_class = filtersets.IPAddressFilterSet From 9691bb29b6ce8c3fb4cebcd6d902fd5977e0d350 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Mon, 29 Apr 2024 09:34:29 -0700 Subject: [PATCH 100/303] 15872 don't escape BANNER_MAINTENANCE (#15885) * 15872 don't escape BANNER_MAINTENANCE * 15872 don't escape BANNER_MAINTENANCE --- netbox/templates/base/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index bb3bbc0e1..53dec6369 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -81,7 +81,7 @@ Blocks: {% if config.MAINTENANCE_MODE and config.BANNER_MAINTENANCE %} {% endif %} From 3cbade536e8925e575e017a18240b85bcafe4200 Mon Sep 17 00:00:00 2001 From: JCWasmx86 Date: Mon, 29 Apr 2024 18:46:39 +0200 Subject: [PATCH 101/303] =?UTF-8?q?Fixes=20#15812:=20Add=20Date(Time)Var?= =?UTF-8?q?=20for=20scripts=20to=20allow=20much=20easier=20date=E2=80=A6?= =?UTF-8?q?=20(#15821)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixes #15812: Add Date(Time)Var for scripts to allow much easier date input * Extend tests for invalid data --------- Co-authored-by: Jeremy Stretch --- docs/customization/custom-scripts.md | 8 +++++ netbox/extras/scripts.py | 25 ++++++++++++++++ netbox/extras/tests/test_scripts.py | 45 ++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/docs/customization/custom-scripts.md b/docs/customization/custom-scripts.md index c68bc21f1..76ca7130f 100644 --- a/docs/customization/custom-scripts.md +++ b/docs/customization/custom-scripts.md @@ -285,6 +285,14 @@ An IPv4 or IPv6 network with a mask. Returns a `netaddr.IPNetwork` object. Two a * `min_prefix_length` - Minimum length of the mask * `max_prefix_length` - Maximum length of the mask +### DateVar + +A calendar date. Returns a `datetime.date` object. + +### DateTimeVar + +A complete date & time. Returns a `datetime.datetime` object. + ## Running Custom Scripts !!! note diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 7d86472c9..d47903f88 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -24,6 +24,7 @@ from ipam.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator, from utilities.exceptions import AbortScript, AbortTransaction from utilities.forms import add_blank_choice from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField +from utilities.forms.widgets import DatePicker, DateTimePicker from .context_managers import event_tracking from .forms import ScriptForm @@ -31,6 +32,8 @@ __all__ = ( 'BaseScript', 'BooleanVar', 'ChoiceVar', + 'DateVar', + 'DateTimeVar', 'FileVar', 'IntegerVar', 'IPAddressVar', @@ -172,6 +175,28 @@ class ChoiceVar(ScriptVariable): self.field_attrs['choices'] = add_blank_choice(choices) +class DateVar(ScriptVariable): + """ + A date. + """ + form_field = forms.DateField + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form_field.widget = DatePicker() + + +class DateTimeVar(ScriptVariable): + """ + A date and a time. + """ + form_field = forms.DateTimeField + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form_field.widget = DateTimePicker() + + class MultiChoiceVar(ScriptVariable): """ Like ChoiceVar, but allows for the selection of multiple choices. diff --git a/netbox/extras/tests/test_scripts.py b/netbox/extras/tests/test_scripts.py index 64971f1dc..bed8f0fc5 100644 --- a/netbox/extras/tests/test_scripts.py +++ b/netbox/extras/tests/test_scripts.py @@ -1,4 +1,5 @@ import tempfile +from datetime import date, datetime, timezone from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase @@ -322,3 +323,47 @@ class ScriptVariablesTest(TestCase): form = TestScript().as_form(data, None) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['var1'], IPNetwork(data['var1'])) + + def test_datevar(self): + + class TestScript(Script): + + var1 = DateVar() + var2 = DateVar(required=False) + + # Test date validation + data = {'var1': 'not a date'} + form = TestScript().as_form(data, None) + self.assertFalse(form.is_valid()) + self.assertIn('var1', form.errors) + + # Validate valid data + input_date = date(2024, 4, 1) + data = {'var1': input_date} + form = TestScript().as_form(data, None) + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['var1'], input_date) + # Validate required=False works for this Var type + self.assertEqual(form.cleaned_data['var2'], None) + + def test_datetimevar(self): + + class TestScript(Script): + + var1 = DateTimeVar() + var2 = DateTimeVar(required=False) + + # Test datetime validation + data = {'var1': 'not a datetime'} + form = TestScript().as_form(data, None) + self.assertFalse(form.is_valid()) + self.assertIn('var1', form.errors) + + # Validate valid data + input_datetime = datetime(2024, 4, 1, 8, 0, 0, 0, timezone.utc) + data = {'var1': input_datetime} + form = TestScript().as_form(data, None) + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['var1'], input_datetime) + # Validate required=False works for this Var type + self.assertEqual(form.cleaned_data['var2'], None) From cbfed83f60f75a788c6e8d4b633c8727ab83aede Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Mon, 29 Apr 2024 10:19:57 -0700 Subject: [PATCH 102/303] 15524 round iprange utilization (#15734) --- netbox/ipam/models/ip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index ca9592d6e..e7fa90d8f 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -692,7 +692,7 @@ class IPRange(PrimaryModel): ip.address.ip for ip in self.get_child_ips() ]).size - return int(float(child_count) / self.size * 100) + return min(float(child_count) / self.size * 100, 100) class IPAddress(PrimaryModel): From 0e3c35ae58cc1d128d90f26f14926657a87205cc Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 29 Apr 2024 13:07:48 -0400 Subject: [PATCH 103/303] Fixes #15548: Ignore many-to-many mappings when checking dependencies of an object being deleted --- netbox/netbox/views/generic/object_views.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 6277e1c5b..031b23b8f 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -339,10 +339,14 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): # Compile a mapping of models to instances dependent_objects = defaultdict(list) - for model, instance in collector.instances_with_model(): + for model, instances in collector.instances_with_model(): + # Ignore relations to auto-created models (e.g. many-to-many mappings) + if model._meta.auto_created: + continue # Omit the root object - if instance != obj: - dependent_objects[model].append(instance) + if instances == obj: + continue + dependent_objects[model].append(instances) return dict(dependent_objects) From 79b9dc20132221f09bf1cb1d7da126bfa24ba9af Mon Sep 17 00:00:00 2001 From: Julio Oliveira at Encora <149191228+Julio-Oliveira-Encora@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:15:44 -0300 Subject: [PATCH 104/303] Feature #15428 - Show all devices with configuration template attached (#15822) * Added devices instances column for config templates. * Added devices instances column for config templates. * Add counts for VMs, roles, and platforms --------- Co-authored-by: Jeremy Stretch --- netbox/extras/tables/tables.py | 26 +++++++++++++++++++++++--- netbox/extras/views.py | 9 ++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 8482c5e24..621dfd26a 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -414,15 +414,35 @@ class ConfigTemplateTable(NetBoxTable): tags = columns.TagColumn( url_name='extras:configtemplate_list' ) + role_count = columns.LinkedCountColumn( + viewname='dcim:devicerole_list', + url_params={'config_template_id': 'pk'}, + verbose_name=_('Device Roles') + ) + platform_count = columns.LinkedCountColumn( + viewname='dcim:platform_list', + url_params={'config_template_id': 'pk'}, + verbose_name=_('Platforms') + ) + device_count = columns.LinkedCountColumn( + viewname='dcim:device_list', + url_params={'config_template_id': 'pk'}, + verbose_name=_('Devices') + ) + vm_count = columns.LinkedCountColumn( + viewname='virtualization:virtualmachine_list', + url_params={'config_template_id': 'pk'}, + verbose_name=_('Virtual Machines') + ) class Meta(NetBoxTable.Meta): model = ConfigTemplate fields = ( - 'pk', 'id', 'name', 'description', 'data_source', 'data_file', 'data_synced', 'created', 'last_updated', - 'tags', + 'pk', 'id', 'name', 'description', 'data_source', 'data_file', 'data_synced', 'role_count', + 'platform_count', 'device_count', 'vm_count', 'created', 'last_updated', 'tags', ) default_columns = ( - 'pk', 'name', 'description', 'is_synced', + 'pk', 'name', 'description', 'is_synced', 'device_count', 'vm_count', ) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 2bf5f349b..6938ccf41 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -13,6 +13,7 @@ from core.choices import JobStatusChoices, ManagedFileRootPathChoices from core.forms import ManagedFileForm from core.models import Job from core.tables import JobTable +from dcim.models import Device, DeviceRole, Platform from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm from extras.dashboard.utils import get_widget_class from netbox.constants import DEFAULT_ACTION_PERMISSIONS @@ -24,6 +25,7 @@ from utilities.rqworker import get_workers_for_queue from utilities.templatetags.builtins.filters import render_markdown from utilities.utils import copy_safe_request, count_related, get_viewname, normalize_querydict, shallow_compare_dict from utilities.views import ContentTypePermissionRequiredMixin, register_model_view +from virtualization.models import VirtualMachine from . import filtersets, forms, tables from .forms.reports import ReportForm from .models import * @@ -624,7 +626,12 @@ class ObjectConfigContextView(generic.ObjectView): # class ConfigTemplateListView(generic.ObjectListView): - queryset = ConfigTemplate.objects.all() + queryset = ConfigTemplate.objects.annotate( + device_count=count_related(Device, 'config_template'), + vm_count=count_related(VirtualMachine, 'config_template'), + role_count=count_related(DeviceRole, 'config_template'), + platform_count=count_related(Platform, 'config_template'), + ) filterset = filtersets.ConfigTemplateFilterSet filterset_form = forms.ConfigTemplateFilterForm table = tables.ConfigTemplateTable From 4b21cf604b351f029b398b7811e38cb103b148e0 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 29 Apr 2024 09:44:02 -0700 Subject: [PATCH 105/303] 14852 delete event-rule when delete script --- netbox/extras/api/serializers.py | 7 +++++-- netbox/extras/models/scripts.py | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 8f00e11d9..43618506d 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -89,8 +89,11 @@ class EventRuleSerializer(NetBoxModelSerializer): # We need to manually instantiate the serializer for scripts if instance.action_type == EventRuleActionChoices.SCRIPT: script_name = instance.action_parameters['script_name'] - script = instance.action_object.scripts[script_name]() - return NestedScriptSerializer(script, context=context).data + if script_name in instance.action_object.scripts: + script = instance.action_object.scripts[script_name]() + return NestedScriptSerializer(script, context=context).data + else: + return None else: serializer = get_serializer_for_model( model=instance.action_object_type.model_class(), diff --git a/netbox/extras/models/scripts.py b/netbox/extras/models/scripts.py index 93275acda..0a0d6541b 100644 --- a/netbox/extras/models/scripts.py +++ b/netbox/extras/models/scripts.py @@ -2,6 +2,7 @@ import inspect import logging from functools import cached_property +from django.contrib.contenttypes.fields import GenericRelation from django.db import models from django.urls import reverse from django.utils.translation import gettext_lazy as _ @@ -41,6 +42,13 @@ class ScriptModule(PythonModuleMixin, JobsMixin, ManagedFile): """ objects = ScriptModuleManager() + event_rules = GenericRelation( + to='extras.EventRule', + content_type_field='action_object_type', + object_id_field='action_object_id', + for_concrete_model=False + ) + class Meta: proxy = True verbose_name = _('script module') From c73a974fa9ee39c08390f865286401cfbd889f21 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 29 Apr 2024 15:26:49 -0400 Subject: [PATCH 106/303] Closes #15811: Note potential incompatibilities for remote auth headers containing underscores --- contrib/gunicorn.py | 4 ++++ docs/administration/authentication/overview.md | 5 ++++- docs/configuration/remote-authentication.md | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/contrib/gunicorn.py b/contrib/gunicorn.py index 89d6943b4..4b2b7c6b0 100644 --- a/contrib/gunicorn.py +++ b/contrib/gunicorn.py @@ -14,3 +14,7 @@ timeout = 120 # The maximum number of requests a worker can handle before being respawned max_requests = 5000 max_requests_jitter = 500 + +# Uncomment this line to accept HTTP headers containing underscores, e.g. for remote +# authentication support. See https://docs.gunicorn.org/en/stable/settings.html#header-map +# header-map = 'dangerous' diff --git a/docs/administration/authentication/overview.md b/docs/administration/authentication/overview.md index f81a50c0b..0c8858b2f 100644 --- a/docs/administration/authentication/overview.md +++ b/docs/administration/authentication/overview.md @@ -26,7 +26,10 @@ REMOTE_AUTH_BACKEND = 'netbox.authentication.RemoteUserBackend' Another option for remote authentication in NetBox is to enable HTTP header-based user assignment. The front end HTTP server (e.g. nginx or Apache) performs client authentication as a process external to NetBox, and passes information about the authenticated user via HTTP headers. By default, the user is assigned via the `REMOTE_USER` header, but this can be customized via the `REMOTE_AUTH_HEADER` configuration parameter. -Optionally, user profile information can be supplied by `REMOTE_USER_FIRST_NAME`, `REMOTE_USER_LAST_NAME` and `REMOTE_USER_EMAIL` headers. These are saved to the users profile during the authentication process. These headers can be customized like the `REMOTE_USER` header. +Optionally, user profile information can be supplied by `REMOTE_USER_FIRST_NAME`, `REMOTE_USER_LAST_NAME` and `REMOTE_USER_EMAIL` headers. These are saved to the user's profile during the authentication process. These headers can be customized like the `REMOTE_USER` header. + +!!! warning Verify Header Compatibility + Some WSGI servers may drop headers which contain unsupported characters. For instance, gunicorn v22.0 and later silently drops HTTP headers containing underscores. This behavior can be disabled by changing gunicorn's [`header_map`](https://docs.gunicorn.org/en/stable/settings.html#header-map) setting to `dangerous`. ### Single Sign-On (SSO) diff --git a/docs/configuration/remote-authentication.md b/docs/configuration/remote-authentication.md index e7fe56a09..5f28d987f 100644 --- a/docs/configuration/remote-authentication.md +++ b/docs/configuration/remote-authentication.md @@ -85,6 +85,9 @@ Default: `'HTTP_REMOTE_USER'` When remote user authentication is in use, this is the name of the HTTP header which informs NetBox of the currently authenticated user. For example, to use the request header `X-Remote-User` it needs to be set to `HTTP_X_REMOTE_USER`. (Requires `REMOTE_AUTH_ENABLED`.) +!!! warning Verify Header Compatibility + Some WSGI servers may drop headers which contain unsupported characters. For instance, gunicorn v22.0 and later silently drops HTTP headers containing underscores. This behavior can be disabled by changing gunicorn's [`header_map`](https://docs.gunicorn.org/en/stable/settings.html#header-map) setting to `dangerous`. + --- ## REMOTE_AUTH_USER_EMAIL From 693c6e4da525da24e6e2f38238feea293e297ac6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 29 Apr 2024 17:55:14 -0400 Subject: [PATCH 107/303] Changelog for #14852, #15428, #15524, #15548, #15812, #15845, #15872 --- docs/release-notes/version-3.7.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/release-notes/version-3.7.md b/docs/release-notes/version-3.7.md index 31b99fcdc..921739e1d 100644 --- a/docs/release-notes/version-3.7.md +++ b/docs/release-notes/version-3.7.md @@ -2,12 +2,22 @@ ## v3.7.7 (FUTURE) +### Enhancements + +* [#15428](https://github.com/netbox-community/netbox/issues/15428) - Show usage counts for associated objects on config template list +* [#15812](https://github.com/netbox-community/netbox/issues/15812) - Add Date & DateTime variable types for custom scripts + ### Bug Fixes * [#13712](https://github.com/netbox-community/netbox/issues/13712) - Fix row highlighting for device interface list display * [#13806](https://github.com/netbox-community/netbox/issues/13806) - Fix "mark" button tooltip on button activation for device interface list display * [#13922](https://github.com/netbox-community/netbox/issues/13922) - Fix SVG drawing error on multiple termination trace with multiple devices * [#14241](https://github.com/netbox-community/netbox/issues/14241) - Fix random interface swap when performing cable trace with multiple termination +* [#14852](https://github.com/netbox-community/netbox/issues/14852) - Fix NoReverseMatch exception when viewing an event rule which references a deleted custom script +* [#15524](https://github.com/netbox-community/netbox/issues/15524) - Fix rounding error when reporting IP range utilization +* [#15548](https://github.com/netbox-community/netbox/issues/15548) - Ignore many-to-many mappings when checking dependencies of an object being deleted +* [#15845](https://github.com/netbox-community/netbox/issues/15845) - Avoid extraneous database queries when fetching assigned IP addresses via REST API +* [#15872](https://github.com/netbox-community/netbox/issues/15872) - `BANNER_MAINTENANCE` content should permit custom HTML --- From 11816b45e7b3f6c056b79339b5eaede069afd833 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 30 Apr 2024 14:41:10 -0400 Subject: [PATCH 108/303] Fixes #15899: Correct the view name for the tags column on L2VPNTerminationTable --- netbox/vpn/tables/l2vpn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/vpn/tables/l2vpn.py b/netbox/vpn/tables/l2vpn.py index 91fddbd66..9a614ab98 100644 --- a/netbox/vpn/tables/l2vpn.py +++ b/netbox/vpn/tables/l2vpn.py @@ -74,7 +74,7 @@ class L2VPNTerminationTable(NetBoxTable): verbose_name=_('Object Site') ) tags = columns.TagColumn( - url_name='ipam:l2vpntermination_list' + url_name='vpn:l2vpntermination_list' ) class Meta(NetBoxTable.Meta): From 365bb4ba1778cdc8dc4f159b1768622fd8974053 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 30 Apr 2024 16:13:17 -0400 Subject: [PATCH 109/303] Fixes #15896: Retain proper formatting for JSON custom field default values --- netbox/extras/models/customfields.py | 3 ++- netbox/netbox/forms/base.py | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/netbox/extras/models/customfields.py b/netbox/extras/models/customfields.py index e78d1af23..c316a1c17 100644 --- a/netbox/extras/models/customfields.py +++ b/netbox/extras/models/customfields.py @@ -1,4 +1,5 @@ import decimal +import json import re from datetime import datetime, date @@ -484,7 +485,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): # JSON elif self.type == CustomFieldTypeChoices.TYPE_JSON: - field = JSONField(required=required, initial=initial) + field = JSONField(required=required, initial=json.dumps(initial) if initial else '') # Object elif self.type == CustomFieldTypeChoices.TYPE_OBJECT: diff --git a/netbox/netbox/forms/base.py b/netbox/netbox/forms/base.py index 1a4155aab..84b6fbbca 100644 --- a/netbox/netbox/forms/base.py +++ b/netbox/netbox/forms/base.py @@ -1,3 +1,5 @@ +import json + from django import forms from django.contrib.contenttypes.models import ContentType from django.db.models import Q @@ -34,7 +36,11 @@ class NetBoxModelForm(BootstrapMixin, CheckLastUpdatedMixin, CustomFieldsMixin, def _get_form_field(self, customfield): if self.instance.pk: form_field = customfield.to_form_field(set_initial=False) - form_field.initial = self.instance.custom_field_data.get(customfield.name, None) + initial = self.instance.custom_field_data.get(customfield.name) + if customfield.type == CustomFieldTypeChoices.TYPE_JSON: + form_field.initial = json.dumps(initial) + else: + form_field.initial = initial return form_field return customfield.to_form_field() From d256c04d9c97edb5cbbea5e15717fff26b030f1a Mon Sep 17 00:00:00 2001 From: Mattias Loverot Date: Tue, 30 Apr 2024 16:43:59 +0200 Subject: [PATCH 110/303] Added caching on /api/schema/ endpoint (closes #15894) --- netbox/netbox/urls.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py index 984358911..48028f248 100644 --- a/netbox/netbox/urls.py +++ b/netbox/netbox/urls.py @@ -1,6 +1,7 @@ from django.conf import settings from django.conf.urls import include from django.urls import path +from django.views.decorators.cache import cache_page from django.views.decorators.csrf import csrf_exempt from django.views.static import serve from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView @@ -56,7 +57,13 @@ _patterns = [ path('api/wireless/', include('wireless.api.urls')), path('api/status/', StatusView.as_view(), name='api-status'), - path('api/schema/', SpectacularAPIView.as_view(), name='schema'), + path( + "api/schema/", + cache_page(timeout=86400, key_prefix=f"api_schema_{settings.VERSION}")( + SpectacularAPIView.as_view() + ), + name="schema", + ), path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='api_docs'), path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='api_redocs'), From 0a7d1e29b41c7f3e3e2acbfaa7458417c906c1ec Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 26 Apr 2024 12:45:43 -0700 Subject: [PATCH 111/303] 15823 remove openid from social-auth-core requirement --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 009e9251d..473a63202 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,7 +29,7 @@ psycopg[c,pool]==3.1.18 PyYAML==6.0.1 requests==2.31.0 social-auth-app-django==5.4.0 -social-auth-core[openidconnect]==4.5.3 +social-auth-core==4.5.4 strawberry-graphql==0.227.2 strawberry-graphql-django==0.34.0 svgwrite==1.4.3 From 209f59639768c0a8293d4ee5ef0cc7318fbe8be0 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Wed, 1 May 2024 06:56:46 -0700 Subject: [PATCH 112/303] 15815 convert dashboard widgets for users/groups (#15839) * 15815 convert dashboard widgets for users/groups * 15815 review fixes * 15815 catch DoesNotExist for widget content type * 15815 add logging --- netbox/extras/dashboard/widgets.py | 14 +++++++-- .../0115_convert_dashboard_widgets.py | 29 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 netbox/extras/migrations/0115_convert_dashboard_widgets.py diff --git a/netbox/extras/dashboard/widgets.py b/netbox/extras/dashboard/widgets.py index 23f082ce2..a3d7f05a3 100644 --- a/netbox/extras/dashboard/widgets.py +++ b/netbox/extras/dashboard/widgets.py @@ -1,3 +1,4 @@ +import logging import uuid from functools import cached_property from hashlib import sha256 @@ -32,6 +33,8 @@ __all__ = ( 'WidgetConfigForm', ) +logger = logging.getLogger('netbox.data_backends') + def get_object_type_choices(): return [ @@ -54,8 +57,15 @@ def get_models_from_content_types(content_types): models = [] for content_type_id in content_types: app_label, model_name = content_type_id.split('.') - content_type = ObjectType.objects.get_by_natural_key(app_label, model_name) - models.append(content_type.model_class()) + try: + content_type = ObjectType.objects.get_by_natural_key(app_label, model_name) + if content_type.model_class(): + models.append(content_type.model_class()) + else: + logger.debug(f"Dashboard Widget model_class not found: {app_label}:{model_name}") + except ObjectType.DoesNotExist: + logger.debug(f"Dashboard Widget ObjectType not found: {app_label}:{model_name}") + return models diff --git a/netbox/extras/migrations/0115_convert_dashboard_widgets.py b/netbox/extras/migrations/0115_convert_dashboard_widgets.py new file mode 100644 index 000000000..c85c83ecf --- /dev/null +++ b/netbox/extras/migrations/0115_convert_dashboard_widgets.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.4 on 2024-04-24 20:09 + +from django.db import migrations + + +def update_dashboard_widgets(apps, schema_editor): + Dashboard = apps.get_model('extras', 'Dashboard') + + for dashboard in Dashboard.objects.all(): + for key, widget in dashboard.config.items(): + if models := widget['config'].get('models'): + models = list(map(lambda x: x.replace('users.netboxgroup', 'users.group'), models)) + models = list(map(lambda x: x.replace('users.netboxuser', 'users.user'), models)) + dashboard.config[key]['config']['models'] = models + dashboard.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0114_customfield_add_comments'), + ] + + operations = [ + migrations.RunPython( + code=update_dashboard_widgets, + reverse_code=migrations.RunPython.noop + ), + ] From d0e0dcb65205aaa7621d8ffa02bc2da3d3b1432c Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Wed, 1 May 2024 07:24:17 -0700 Subject: [PATCH 113/303] 15855 fix adding script as event rule (#15861) * 15855 fix adding script as event rule * 15855 fix adding script as event rule * 15855 fix adding script as event rule * 15855 fix adding script as event rule --- netbox/extras/api/serializers_/events.py | 3 +-- netbox/extras/events.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/netbox/extras/api/serializers_/events.py b/netbox/extras/api/serializers_/events.py index 4285b12e6..469da3e8c 100644 --- a/netbox/extras/api/serializers_/events.py +++ b/netbox/extras/api/serializers_/events.py @@ -47,8 +47,7 @@ class EventRuleSerializer(NetBoxModelSerializer): # We need to manually instantiate the serializer for scripts if instance.action_type == EventRuleActionChoices.SCRIPT: script = instance.action_object - instance = script.python_class() if script.python_class else None - return ScriptSerializer(instance, nested=True, context=context).data + return ScriptSerializer(script, nested=True, context=context).data else: serializer = get_serializer_for_model(instance.action_object_type.model_class()) return serializer(instance.action_object, nested=True, context=context).data diff --git a/netbox/extras/events.py b/netbox/extras/events.py index a33ac213c..34d2ec159 100644 --- a/netbox/extras/events.py +++ b/netbox/extras/events.py @@ -118,7 +118,7 @@ def process_event_rules(event_rules, model_name, event, data, username=None, sna # Enqueue a Job to record the script's execution Job.enqueue( "extras.scripts.run_script", - instance=script.module, + instance=event_rule.action_object, name=script.name, user=user, data=data From 778b8b9b48efee3535ccb073fdb1bcb9315188cf Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 26 Apr 2024 11:36:43 -0700 Subject: [PATCH 114/303] 15853 fix background color for cable trace svg in dark mode --- netbox/project-static/dist/cable_trace.css | Bin 1150 -> 1272 bytes .../styles/svg/cable_trace.scss | 5 ++++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/netbox/project-static/dist/cable_trace.css b/netbox/project-static/dist/cable_trace.css index 54f01c3f7492097d5a9cf336c8dfc550462f5ab6..3b9fe7504675825073824d49249f68cb0a0cdf16 100644 GIT binary patch delta 144 zcmeyz@q^Rcswh9dq*_-uFR4Peq$n{tRW~W!N}(*VNJCe*Bq^r|$ki<_ElLB)6sIQV z=cObTRccyKWIHGVQdpFpq+w*PqhMmFqhM;GX}xjx14g~-q{QUx^rHOIycFH!{G9wE dD~Nf>c4*cXm!&Hdr6!k5PGl0>yp8DuBLHnYGS~nB delta 24 gcmeyt`HzFmswh9dqoGCyVn0DRF2U;qFB diff --git a/netbox/project-static/styles/svg/cable_trace.scss b/netbox/project-static/styles/svg/cable_trace.scss index b7b09c219..b146c88aa 100644 --- a/netbox/project-static/styles/svg/cable_trace.scss +++ b/netbox/project-static/styles/svg/cable_trace.scss @@ -8,6 +8,7 @@ :root { // Light mode values + --nbx-trace-bg: var(--tblr-bg-surface-secondary); --nbx-trace-color: #{$black}; --nbx-trace-node-bg: #{$gray-200}; --nbx-trace-termination-bg: #{$gray-100}; @@ -16,6 +17,7 @@ &[data-bs-theme='dark'] { // Dark mode values + --nbx-trace-bg: rgb(27, 41, 58); --nbx-trace-color: #{$white}; --nbx-trace-node-bg: #{$gray-900}; --nbx-trace-termination-bg: #{$gray-800}; @@ -44,6 +46,8 @@ text { } svg { + background-color: var(--nbx-trace-bg); + /* Boxes */ rect { fill: var(--nbx-trace-node-bg); @@ -79,4 +83,3 @@ svg { stroke-dasharray: 5px; } } - From 1add918d313d4b0d1d5dd17e362b061308606e7e Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 30 Apr 2024 15:20:35 -0700 Subject: [PATCH 115/303] 15833 make navbar sticky at top --- netbox/templates/base/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index 9365df43b..a9df01cd5 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -47,7 +47,7 @@ Blocks: {# Top menu #} -
  • `. - // - needLf = false; - } - } - } - } - result += needLf ? '>\n' : '>'; - return result; -}; - -/** - * Renderer.renderInline(tokens, options, env) -> String - * - tokens (Array): list on block tokens to renter - * - options (Object): params of parser instance - * - env (Object): additional data from parsed input (references, for example) - * - * The same as [[Renderer.render]], but for single token of `inline` type. - **/ -Renderer.prototype.renderInline = function (tokens, options, env) { - var type, - result = '', - rules = this.rules; - for (var i = 0, len = tokens.length; i < len; i++) { - type = tokens[i].type; - if (typeof rules[type] !== 'undefined') { - result += rules[type](tokens, i, options, env, this); - } else { - result += this.renderToken(tokens, i, options); - } - } - return result; -}; - -/** internal - * Renderer.renderInlineAsText(tokens, options, env) -> String - * - tokens (Array): list on block tokens to renter - * - options (Object): params of parser instance - * - env (Object): additional data from parsed input (references, for example) - * - * Special kludge for image `alt` attributes to conform CommonMark spec. - * Don't try to use it! Spec requires to show `alt` content with stripped markup, - * instead of simple escaping. - **/ -Renderer.prototype.renderInlineAsText = function (tokens, options, env) { - var result = ''; - for (var i = 0, len = tokens.length; i < len; i++) { - if (tokens[i].type === 'text') { - result += tokens[i].content; - } else if (tokens[i].type === 'image') { - result += this.renderInlineAsText(tokens[i].children, options, env); - } else if (tokens[i].type === 'softbreak') { - result += '\n'; - } - } - return result; -}; - -/** - * Renderer.render(tokens, options, env) -> String - * - tokens (Array): list on block tokens to renter - * - options (Object): params of parser instance - * - env (Object): additional data from parsed input (references, for example) - * - * Takes token stream and generates HTML. Probably, you will never need to call - * this method directly. - **/ -Renderer.prototype.render = function (tokens, options, env) { - var i, - len, - type, - result = '', - rules = this.rules; - for (i = 0, len = tokens.length; i < len; i++) { - type = tokens[i].type; - if (type === 'inline') { - result += this.renderInline(tokens[i].children, options, env); - } else if (typeof rules[type] !== 'undefined') { - result += rules[tokens[i].type](tokens, i, options, env, this); - } else { - result += this.renderToken(tokens, i, options, env); - } - } - return result; -}; -module.exports = Renderer; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/ruler.js": -/*!******************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/ruler.js ***! - \******************************************************/ -/***/ (function(module) { - -/** - * class Ruler - * - * Helper class, used by [[MarkdownIt#core]], [[MarkdownIt#block]] and - * [[MarkdownIt#inline]] to manage sequences of functions (rules): - * - * - keep rules in defined order - * - assign the name to each rule - * - enable/disable rules - * - add/replace rules - * - allow assign rules to additional named chains (in the same) - * - cacheing lists of active rules - * - * You will not need use this class directly until write plugins. For simple - * rules control use [[MarkdownIt.disable]], [[MarkdownIt.enable]] and - * [[MarkdownIt.use]]. - **/ - - -/** - * new Ruler() - **/ -function Ruler() { - // List of added rules. Each element is: - // - // { - // name: XXX, - // enabled: Boolean, - // fn: Function(), - // alt: [ name2, name3 ] - // } - // - this.__rules__ = []; - - // Cached rule chains. - // - // First level - chain name, '' for default. - // Second level - diginal anchor for fast filtering by charcodes. - // - this.__cache__ = null; -} - -//////////////////////////////////////////////////////////////////////////////// -// Helper methods, should not be used directly - -// Find rule index by name -// -Ruler.prototype.__find__ = function (name) { - for (var i = 0; i < this.__rules__.length; i++) { - if (this.__rules__[i].name === name) { - return i; - } - } - return -1; -}; - -// Build rules lookup cache -// -Ruler.prototype.__compile__ = function () { - var self = this; - var chains = ['']; - - // collect unique names - self.__rules__.forEach(function (rule) { - if (!rule.enabled) { - return; - } - rule.alt.forEach(function (altName) { - if (chains.indexOf(altName) < 0) { - chains.push(altName); - } - }); - }); - self.__cache__ = {}; - chains.forEach(function (chain) { - self.__cache__[chain] = []; - self.__rules__.forEach(function (rule) { - if (!rule.enabled) { - return; - } - if (chain && rule.alt.indexOf(chain) < 0) { - return; - } - self.__cache__[chain].push(rule.fn); - }); - }); -}; - -/** - * Ruler.at(name, fn [, options]) - * - name (String): rule name to replace. - * - fn (Function): new rule function. - * - options (Object): new rule options (not mandatory). - * - * Replace rule by name with new function & options. Throws error if name not - * found. - * - * ##### Options: - * - * - __alt__ - array with names of "alternate" chains. - * - * ##### Example - * - * Replace existing typographer replacement rule with new one: - * - * ```javascript - * var md = require('markdown-it')(); - * - * md.core.ruler.at('replacements', function replace(state) { - * //... - * }); - * ``` - **/ -Ruler.prototype.at = function (name, fn, options) { - var index = this.__find__(name); - var opt = options || {}; - if (index === -1) { - throw new Error('Parser rule not found: ' + name); - } - this.__rules__[index].fn = fn; - this.__rules__[index].alt = opt.alt || []; - this.__cache__ = null; -}; - -/** - * Ruler.before(beforeName, ruleName, fn [, options]) - * - beforeName (String): new rule will be added before this one. - * - ruleName (String): name of added rule. - * - fn (Function): rule function. - * - options (Object): rule options (not mandatory). - * - * Add new rule to chain before one with given name. See also - * [[Ruler.after]], [[Ruler.push]]. - * - * ##### Options: - * - * - __alt__ - array with names of "alternate" chains. - * - * ##### Example - * - * ```javascript - * var md = require('markdown-it')(); - * - * md.block.ruler.before('paragraph', 'my_rule', function replace(state) { - * //... - * }); - * ``` - **/ -Ruler.prototype.before = function (beforeName, ruleName, fn, options) { - var index = this.__find__(beforeName); - var opt = options || {}; - if (index === -1) { - throw new Error('Parser rule not found: ' + beforeName); - } - this.__rules__.splice(index, 0, { - name: ruleName, - enabled: true, - fn: fn, - alt: opt.alt || [] - }); - this.__cache__ = null; -}; - -/** - * Ruler.after(afterName, ruleName, fn [, options]) - * - afterName (String): new rule will be added after this one. - * - ruleName (String): name of added rule. - * - fn (Function): rule function. - * - options (Object): rule options (not mandatory). - * - * Add new rule to chain after one with given name. See also - * [[Ruler.before]], [[Ruler.push]]. - * - * ##### Options: - * - * - __alt__ - array with names of "alternate" chains. - * - * ##### Example - * - * ```javascript - * var md = require('markdown-it')(); - * - * md.inline.ruler.after('text', 'my_rule', function replace(state) { - * //... - * }); - * ``` - **/ -Ruler.prototype.after = function (afterName, ruleName, fn, options) { - var index = this.__find__(afterName); - var opt = options || {}; - if (index === -1) { - throw new Error('Parser rule not found: ' + afterName); - } - this.__rules__.splice(index + 1, 0, { - name: ruleName, - enabled: true, - fn: fn, - alt: opt.alt || [] - }); - this.__cache__ = null; -}; - -/** - * Ruler.push(ruleName, fn [, options]) - * - ruleName (String): name of added rule. - * - fn (Function): rule function. - * - options (Object): rule options (not mandatory). - * - * Push new rule to the end of chain. See also - * [[Ruler.before]], [[Ruler.after]]. - * - * ##### Options: - * - * - __alt__ - array with names of "alternate" chains. - * - * ##### Example - * - * ```javascript - * var md = require('markdown-it')(); - * - * md.core.ruler.push('my_rule', function replace(state) { - * //... - * }); - * ``` - **/ -Ruler.prototype.push = function (ruleName, fn, options) { - var opt = options || {}; - this.__rules__.push({ - name: ruleName, - enabled: true, - fn: fn, - alt: opt.alt || [] - }); - this.__cache__ = null; -}; - -/** - * Ruler.enable(list [, ignoreInvalid]) -> Array - * - list (String|Array): list of rule names to enable. - * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. - * - * Enable rules with given names. If any rule name not found - throw Error. - * Errors can be disabled by second param. - * - * Returns list of found rule names (if no exception happened). - * - * See also [[Ruler.disable]], [[Ruler.enableOnly]]. - **/ -Ruler.prototype.enable = function (list, ignoreInvalid) { - if (!Array.isArray(list)) { - list = [list]; - } - var result = []; - - // Search by name and enable - list.forEach(function (name) { - var idx = this.__find__(name); - if (idx < 0) { - if (ignoreInvalid) { - return; - } - throw new Error('Rules manager: invalid rule name ' + name); - } - this.__rules__[idx].enabled = true; - result.push(name); - }, this); - this.__cache__ = null; - return result; -}; - -/** - * Ruler.enableOnly(list [, ignoreInvalid]) - * - list (String|Array): list of rule names to enable (whitelist). - * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. - * - * Enable rules with given names, and disable everything else. If any rule name - * not found - throw Error. Errors can be disabled by second param. - * - * See also [[Ruler.disable]], [[Ruler.enable]]. - **/ -Ruler.prototype.enableOnly = function (list, ignoreInvalid) { - if (!Array.isArray(list)) { - list = [list]; - } - this.__rules__.forEach(function (rule) { - rule.enabled = false; - }); - this.enable(list, ignoreInvalid); -}; - -/** - * Ruler.disable(list [, ignoreInvalid]) -> Array - * - list (String|Array): list of rule names to disable. - * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. - * - * Disable rules with given names. If any rule name not found - throw Error. - * Errors can be disabled by second param. - * - * Returns list of found rule names (if no exception happened). - * - * See also [[Ruler.enable]], [[Ruler.enableOnly]]. - **/ -Ruler.prototype.disable = function (list, ignoreInvalid) { - if (!Array.isArray(list)) { - list = [list]; - } - var result = []; - - // Search by name and disable - list.forEach(function (name) { - var idx = this.__find__(name); - if (idx < 0) { - if (ignoreInvalid) { - return; - } - throw new Error('Rules manager: invalid rule name ' + name); - } - this.__rules__[idx].enabled = false; - result.push(name); - }, this); - this.__cache__ = null; - return result; -}; - -/** - * Ruler.getRules(chainName) -> Array - * - * Return array of active functions (rules) for given chain name. It analyzes - * rules configuration, compiles caches if not exists and returns result. - * - * Default chain name is `''` (empty string). It can't be skipped. That's - * done intentionally, to keep signature monomorphic for high speed. - **/ -Ruler.prototype.getRules = function (chainName) { - if (this.__cache__ === null) { - this.__compile__(); - } - - // Chain can be empty, if rules disabled. But we still have to return Array. - return this.__cache__[chainName] || []; -}; -module.exports = Ruler; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/blockquote.js": -/*!***********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/blockquote.js ***! - \***********************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Block quotes - - - -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -module.exports = function blockquote(state, startLine, endLine, silent) { - var adjustTab, - ch, - i, - initial, - l, - lastLineEmpty, - lines, - nextLine, - offset, - oldBMarks, - oldBSCount, - oldIndent, - oldParentType, - oldSCount, - oldTShift, - spaceAfterMarker, - terminate, - terminatorRules, - token, - isOutdented, - oldLineMax = state.lineMax, - pos = state.bMarks[startLine] + state.tShift[startLine], - max = state.eMarks[startLine]; - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) { - return false; - } - - // check the block quote marker - if (state.src.charCodeAt(pos++) !== 0x3E /* > */) { - return false; - } - - // we know that it's going to be a valid blockquote, - // so no point trying to find the end of it in silent mode - if (silent) { - return true; - } - - // set offset past spaces and ">" - initial = offset = state.sCount[startLine] + 1; - - // skip one optional space after '>' - if (state.src.charCodeAt(pos) === 0x20 /* space */) { - // ' > test ' - // ^ -- position start of line here: - pos++; - initial++; - offset++; - adjustTab = false; - spaceAfterMarker = true; - } else if (state.src.charCodeAt(pos) === 0x09 /* tab */) { - spaceAfterMarker = true; - if ((state.bsCount[startLine] + offset) % 4 === 3) { - // ' >\t test ' - // ^ -- position start of line here (tab has width===1) - pos++; - initial++; - offset++; - adjustTab = false; - } else { - // ' >\t test ' - // ^ -- position start of line here + shift bsCount slightly - // to make extra space appear - adjustTab = true; - } - } else { - spaceAfterMarker = false; - } - oldBMarks = [state.bMarks[startLine]]; - state.bMarks[startLine] = pos; - while (pos < max) { - ch = state.src.charCodeAt(pos); - if (isSpace(ch)) { - if (ch === 0x09) { - offset += 4 - (offset + state.bsCount[startLine] + (adjustTab ? 1 : 0)) % 4; - } else { - offset++; - } - } else { - break; - } - pos++; - } - oldBSCount = [state.bsCount[startLine]]; - state.bsCount[startLine] = state.sCount[startLine] + 1 + (spaceAfterMarker ? 1 : 0); - lastLineEmpty = pos >= max; - oldSCount = [state.sCount[startLine]]; - state.sCount[startLine] = offset - initial; - oldTShift = [state.tShift[startLine]]; - state.tShift[startLine] = pos - state.bMarks[startLine]; - terminatorRules = state.md.block.ruler.getRules('blockquote'); - oldParentType = state.parentType; - state.parentType = 'blockquote'; - - // Search the end of the block - // - // Block ends with either: - // 1. an empty line outside: - // ``` - // > test - // - // ``` - // 2. an empty line inside: - // ``` - // > - // test - // ``` - // 3. another tag: - // ``` - // > test - // - - - - // ``` - for (nextLine = startLine + 1; nextLine < endLine; nextLine++) { - // check if it's outdented, i.e. it's inside list item and indented - // less than said list item: - // - // ``` - // 1. anything - // > current blockquote - // 2. checking this line - // ``` - isOutdented = state.sCount[nextLine] < state.blkIndent; - pos = state.bMarks[nextLine] + state.tShift[nextLine]; - max = state.eMarks[nextLine]; - if (pos >= max) { - // Case 1: line is not inside the blockquote, and this line is empty. - break; - } - if (state.src.charCodeAt(pos++) === 0x3E /* > */ && !isOutdented) { - // This line is inside the blockquote. - - // set offset past spaces and ">" - initial = offset = state.sCount[nextLine] + 1; - - // skip one optional space after '>' - if (state.src.charCodeAt(pos) === 0x20 /* space */) { - // ' > test ' - // ^ -- position start of line here: - pos++; - initial++; - offset++; - adjustTab = false; - spaceAfterMarker = true; - } else if (state.src.charCodeAt(pos) === 0x09 /* tab */) { - spaceAfterMarker = true; - if ((state.bsCount[nextLine] + offset) % 4 === 3) { - // ' >\t test ' - // ^ -- position start of line here (tab has width===1) - pos++; - initial++; - offset++; - adjustTab = false; - } else { - // ' >\t test ' - // ^ -- position start of line here + shift bsCount slightly - // to make extra space appear - adjustTab = true; - } - } else { - spaceAfterMarker = false; - } - oldBMarks.push(state.bMarks[nextLine]); - state.bMarks[nextLine] = pos; - while (pos < max) { - ch = state.src.charCodeAt(pos); - if (isSpace(ch)) { - if (ch === 0x09) { - offset += 4 - (offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4; - } else { - offset++; - } - } else { - break; - } - pos++; - } - lastLineEmpty = pos >= max; - oldBSCount.push(state.bsCount[nextLine]); - state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0); - oldSCount.push(state.sCount[nextLine]); - state.sCount[nextLine] = offset - initial; - oldTShift.push(state.tShift[nextLine]); - state.tShift[nextLine] = pos - state.bMarks[nextLine]; - continue; - } - - // Case 2: line is not inside the blockquote, and the last line was empty. - if (lastLineEmpty) { - break; - } - - // Case 3: another tag found. - terminate = false; - for (i = 0, l = terminatorRules.length; i < l; i++) { - if (terminatorRules[i](state, nextLine, endLine, true)) { - terminate = true; - break; - } - } - if (terminate) { - // Quirk to enforce "hard termination mode" for paragraphs; - // normally if you call `tokenize(state, startLine, nextLine)`, - // paragraphs will look below nextLine for paragraph continuation, - // but if blockquote is terminated by another tag, they shouldn't - state.lineMax = nextLine; - if (state.blkIndent !== 0) { - // state.blkIndent was non-zero, we now set it to zero, - // so we need to re-calculate all offsets to appear as - // if indent wasn't changed - oldBMarks.push(state.bMarks[nextLine]); - oldBSCount.push(state.bsCount[nextLine]); - oldTShift.push(state.tShift[nextLine]); - oldSCount.push(state.sCount[nextLine]); - state.sCount[nextLine] -= state.blkIndent; - } - break; - } - oldBMarks.push(state.bMarks[nextLine]); - oldBSCount.push(state.bsCount[nextLine]); - oldTShift.push(state.tShift[nextLine]); - oldSCount.push(state.sCount[nextLine]); - - // A negative indentation means that this is a paragraph continuation - // - state.sCount[nextLine] = -1; - } - oldIndent = state.blkIndent; - state.blkIndent = 0; - token = state.push('blockquote_open', 'blockquote', 1); - token.markup = '>'; - token.map = lines = [startLine, 0]; - state.md.block.tokenize(state, startLine, nextLine); - token = state.push('blockquote_close', 'blockquote', -1); - token.markup = '>'; - state.lineMax = oldLineMax; - state.parentType = oldParentType; - lines[1] = state.line; - - // Restore original tShift; this might not be necessary since the parser - // has already been here, but just to make sure we can do that. - for (i = 0; i < oldTShift.length; i++) { - state.bMarks[i + startLine] = oldBMarks[i]; - state.tShift[i + startLine] = oldTShift[i]; - state.sCount[i + startLine] = oldSCount[i]; - state.bsCount[i + startLine] = oldBSCount[i]; - } - state.blkIndent = oldIndent; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/code.js": -/*!*****************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/code.js ***! - \*****************************************************************/ -/***/ (function(module) { - -// Code block (4 spaces padded) - - - -module.exports = function code(state, startLine, endLine /*, silent*/) { - var nextLine, last, token; - if (state.sCount[startLine] - state.blkIndent < 4) { - return false; - } - last = nextLine = startLine + 1; - while (nextLine < endLine) { - if (state.isEmpty(nextLine)) { - nextLine++; - continue; - } - if (state.sCount[nextLine] - state.blkIndent >= 4) { - nextLine++; - last = nextLine; - continue; - } - break; - } - state.line = last; - token = state.push('code_block', 'code', 0); - token.content = state.getLines(startLine, last, 4 + state.blkIndent, false) + '\n'; - token.map = [startLine, state.line]; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/fence.js": -/*!******************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/fence.js ***! - \******************************************************************/ -/***/ (function(module) { - -// fences (``` lang, ~~~ lang) - - - -module.exports = function fence(state, startLine, endLine, silent) { - var marker, - len, - params, - nextLine, - mem, - token, - markup, - haveEndMarker = false, - pos = state.bMarks[startLine] + state.tShift[startLine], - max = state.eMarks[startLine]; - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) { - return false; - } - if (pos + 3 > max) { - return false; - } - marker = state.src.charCodeAt(pos); - if (marker !== 0x7E /* ~ */ && marker !== 0x60 /* ` */) { - return false; - } - - // scan marker length - mem = pos; - pos = state.skipChars(pos, marker); - len = pos - mem; - if (len < 3) { - return false; - } - markup = state.src.slice(mem, pos); - params = state.src.slice(pos, max); - if (marker === 0x60 /* ` */) { - if (params.indexOf(String.fromCharCode(marker)) >= 0) { - return false; - } - } - - // Since start is found, we can report success here in validation mode - if (silent) { - return true; - } - - // search end of block - nextLine = startLine; - for (;;) { - nextLine++; - if (nextLine >= endLine) { - // unclosed block should be autoclosed by end of document. - // also block seems to be autoclosed by end of parent - break; - } - pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]; - max = state.eMarks[nextLine]; - if (pos < max && state.sCount[nextLine] < state.blkIndent) { - // non-empty line with negative indent should stop the list: - // - ``` - // test - break; - } - if (state.src.charCodeAt(pos) !== marker) { - continue; - } - if (state.sCount[nextLine] - state.blkIndent >= 4) { - // closing fence should be indented less than 4 spaces - continue; - } - pos = state.skipChars(pos, marker); - - // closing code fence must be at least as long as the opening one - if (pos - mem < len) { - continue; - } - - // make sure tail has spaces only - pos = state.skipSpaces(pos); - if (pos < max) { - continue; - } - haveEndMarker = true; - // found! - break; - } - - // If a fence has heading spaces, they should be removed from its inner block - len = state.sCount[startLine]; - state.line = nextLine + (haveEndMarker ? 1 : 0); - token = state.push('fence', 'code', 0); - token.info = params; - token.content = state.getLines(startLine + 1, nextLine, len, true); - token.markup = markup; - token.map = [startLine, state.line]; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/heading.js": -/*!********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/heading.js ***! - \********************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// heading (#, ##, ...) - - - -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -module.exports = function heading(state, startLine, endLine, silent) { - var ch, - level, - tmp, - token, - pos = state.bMarks[startLine] + state.tShift[startLine], - max = state.eMarks[startLine]; - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) { - return false; - } - ch = state.src.charCodeAt(pos); - if (ch !== 0x23 /* # */ || pos >= max) { - return false; - } - - // count heading level - level = 1; - ch = state.src.charCodeAt(++pos); - while (ch === 0x23 /* # */ && pos < max && level <= 6) { - level++; - ch = state.src.charCodeAt(++pos); - } - if (level > 6 || pos < max && !isSpace(ch)) { - return false; - } - if (silent) { - return true; - } - - // Let's cut tails like ' ### ' from the end of string - - max = state.skipSpacesBack(max, pos); - tmp = state.skipCharsBack(max, 0x23, pos); // # - if (tmp > pos && isSpace(state.src.charCodeAt(tmp - 1))) { - max = tmp; - } - state.line = startLine + 1; - token = state.push('heading_open', 'h' + String(level), 1); - token.markup = '########'.slice(0, level); - token.map = [startLine, state.line]; - token = state.push('inline', '', 0); - token.content = state.src.slice(pos, max).trim(); - token.map = [startLine, state.line]; - token.children = []; - token = state.push('heading_close', 'h' + String(level), -1); - token.markup = '########'.slice(0, level); - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/hr.js": -/*!***************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/hr.js ***! - \***************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Horizontal rule - - - -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -module.exports = function hr(state, startLine, endLine, silent) { - var marker, - cnt, - ch, - token, - pos = state.bMarks[startLine] + state.tShift[startLine], - max = state.eMarks[startLine]; - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) { - return false; - } - marker = state.src.charCodeAt(pos++); - - // Check hr marker - if (marker !== 0x2A /* * */ && marker !== 0x2D /* - */ && marker !== 0x5F /* _ */) { - return false; - } - - // markers can be mixed with spaces, but there should be at least 3 of them - - cnt = 1; - while (pos < max) { - ch = state.src.charCodeAt(pos++); - if (ch !== marker && !isSpace(ch)) { - return false; - } - if (ch === marker) { - cnt++; - } - } - if (cnt < 3) { - return false; - } - if (silent) { - return true; - } - state.line = startLine + 1; - token = state.push('hr', 'hr', 0); - token.map = [startLine, state.line]; - token.markup = Array(cnt + 1).join(String.fromCharCode(marker)); - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/html_block.js": -/*!***********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/html_block.js ***! - \***********************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// HTML block - - - -var block_names = __webpack_require__(/*! ../common/html_blocks */ "../../../node_modules/markdown-it/lib/common/html_blocks.js"); -var HTML_OPEN_CLOSE_TAG_RE = (__webpack_require__(/*! ../common/html_re */ "../../../node_modules/markdown-it/lib/common/html_re.js").HTML_OPEN_CLOSE_TAG_RE); - -// An array of opening and corresponding closing sequences for html tags, -// last argument defines whether it can terminate a paragraph or not -// -var HTML_SEQUENCES = [[/^<(script|pre|style|textarea)(?=(\s|>|$))/i, /<\/(script|pre|style|textarea)>/i, true], [/^/, true], [/^<\?/, /\?>/, true], [/^/, true], [/^/, true], [new RegExp('^|$))', 'i'), /^$/, true], [new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false]]; -module.exports = function html_block(state, startLine, endLine, silent) { - var i, - nextLine, - token, - lineText, - pos = state.bMarks[startLine] + state.tShift[startLine], - max = state.eMarks[startLine]; - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) { - return false; - } - if (!state.md.options.html) { - return false; - } - if (state.src.charCodeAt(pos) !== 0x3C /* < */) { - return false; - } - lineText = state.src.slice(pos, max); - for (i = 0; i < HTML_SEQUENCES.length; i++) { - if (HTML_SEQUENCES[i][0].test(lineText)) { - break; - } - } - if (i === HTML_SEQUENCES.length) { - return false; - } - if (silent) { - // true if this sequence can be a terminator, false otherwise - return HTML_SEQUENCES[i][2]; - } - nextLine = startLine + 1; - - // If we are here - we detected HTML block. - // Let's roll down till block end. - if (!HTML_SEQUENCES[i][1].test(lineText)) { - for (; nextLine < endLine; nextLine++) { - if (state.sCount[nextLine] < state.blkIndent) { - break; - } - pos = state.bMarks[nextLine] + state.tShift[nextLine]; - max = state.eMarks[nextLine]; - lineText = state.src.slice(pos, max); - if (HTML_SEQUENCES[i][1].test(lineText)) { - if (lineText.length !== 0) { - nextLine++; - } - break; - } - } - } - state.line = nextLine; - token = state.push('html_block', '', 0); - token.map = [startLine, nextLine]; - token.content = state.getLines(startLine, nextLine, state.blkIndent, true); - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/lheading.js": -/*!*********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/lheading.js ***! - \*********************************************************************/ -/***/ (function(module) { - -// lheading (---, ===) - - - -module.exports = function lheading(state, startLine, endLine /*, silent*/) { - var content, - terminate, - i, - l, - token, - pos, - max, - level, - marker, - nextLine = startLine + 1, - oldParentType, - terminatorRules = state.md.block.ruler.getRules('paragraph'); - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) { - return false; - } - oldParentType = state.parentType; - state.parentType = 'paragraph'; // use paragraph to match terminatorRules - - // jump line-by-line until empty one or EOF - for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { - // this would be a code block normally, but after paragraph - // it's considered a lazy continuation regardless of what's there - if (state.sCount[nextLine] - state.blkIndent > 3) { - continue; - } - - // - // Check for underline in setext header - // - if (state.sCount[nextLine] >= state.blkIndent) { - pos = state.bMarks[nextLine] + state.tShift[nextLine]; - max = state.eMarks[nextLine]; - if (pos < max) { - marker = state.src.charCodeAt(pos); - if (marker === 0x2D /* - */ || marker === 0x3D /* = */) { - pos = state.skipChars(pos, marker); - pos = state.skipSpaces(pos); - if (pos >= max) { - level = marker === 0x3D /* = */ ? 1 : 2; - break; - } - } - } - } - - // quirk for blockquotes, this line should already be checked by that rule - if (state.sCount[nextLine] < 0) { - continue; - } - - // Some tags can terminate paragraph without empty line. - terminate = false; - for (i = 0, l = terminatorRules.length; i < l; i++) { - if (terminatorRules[i](state, nextLine, endLine, true)) { - terminate = true; - break; - } - } - if (terminate) { - break; - } - } - if (!level) { - // Didn't find valid underline - return false; - } - content = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); - state.line = nextLine + 1; - token = state.push('heading_open', 'h' + String(level), 1); - token.markup = String.fromCharCode(marker); - token.map = [startLine, state.line]; - token = state.push('inline', '', 0); - token.content = content; - token.map = [startLine, state.line - 1]; - token.children = []; - token = state.push('heading_close', 'h' + String(level), -1); - token.markup = String.fromCharCode(marker); - state.parentType = oldParentType; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/list.js": -/*!*****************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/list.js ***! - \*****************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Lists - - - -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); - -// Search `[-+*][\n ]`, returns next pos after marker on success -// or -1 on fail. -function skipBulletListMarker(state, startLine) { - var marker, pos, max, ch; - pos = state.bMarks[startLine] + state.tShift[startLine]; - max = state.eMarks[startLine]; - marker = state.src.charCodeAt(pos++); - // Check bullet - if (marker !== 0x2A /* * */ && marker !== 0x2D /* - */ && marker !== 0x2B /* + */) { - return -1; - } - if (pos < max) { - ch = state.src.charCodeAt(pos); - if (!isSpace(ch)) { - // " -test " - is not a list item - return -1; - } - } - return pos; -} - -// Search `\d+[.)][\n ]`, returns next pos after marker on success -// or -1 on fail. -function skipOrderedListMarker(state, startLine) { - var ch, - start = state.bMarks[startLine] + state.tShift[startLine], - pos = start, - max = state.eMarks[startLine]; - - // List marker should have at least 2 chars (digit + dot) - if (pos + 1 >= max) { - return -1; - } - ch = state.src.charCodeAt(pos++); - if (ch < 0x30 /* 0 */ || ch > 0x39 /* 9 */) { - return -1; - } - for (;;) { - // EOL -> fail - if (pos >= max) { - return -1; - } - ch = state.src.charCodeAt(pos++); - if (ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */) { - // List marker should have no more than 9 digits - // (prevents integer overflow in browsers) - if (pos - start >= 10) { - return -1; - } - continue; - } - - // found valid marker - if (ch === 0x29 /* ) */ || ch === 0x2e /* . */) { - break; - } - return -1; - } - if (pos < max) { - ch = state.src.charCodeAt(pos); - if (!isSpace(ch)) { - // " 1.test " - is not a list item - return -1; - } - } - return pos; -} -function markTightParagraphs(state, idx) { - var i, - l, - level = state.level + 2; - for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) { - if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') { - state.tokens[i + 2].hidden = true; - state.tokens[i].hidden = true; - i += 2; - } - } -} -module.exports = function list(state, startLine, endLine, silent) { - var ch, - contentStart, - i, - indent, - indentAfterMarker, - initial, - isOrdered, - itemLines, - l, - listLines, - listTokIdx, - markerCharCode, - markerValue, - max, - nextLine, - offset, - oldListIndent, - oldParentType, - oldSCount, - oldTShift, - oldTight, - pos, - posAfterMarker, - prevEmptyEnd, - start, - terminate, - terminatorRules, - token, - isTerminatingParagraph = false, - tight = true; - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) { - return false; - } - - // Special case: - // - item 1 - // - item 2 - // - item 3 - // - item 4 - // - this one is a paragraph continuation - if (state.listIndent >= 0 && state.sCount[startLine] - state.listIndent >= 4 && state.sCount[startLine] < state.blkIndent) { - return false; - } - - // limit conditions when list can interrupt - // a paragraph (validation mode only) - if (silent && state.parentType === 'paragraph') { - // Next list item should still terminate previous list item; - // - // This code can fail if plugins use blkIndent as well as lists, - // but I hope the spec gets fixed long before that happens. - // - if (state.tShift[startLine] >= state.blkIndent) { - isTerminatingParagraph = true; - } - } - - // Detect list type and position after marker - if ((posAfterMarker = skipOrderedListMarker(state, startLine)) >= 0) { - isOrdered = true; - start = state.bMarks[startLine] + state.tShift[startLine]; - markerValue = Number(state.src.slice(start, posAfterMarker - 1)); - - // If we're starting a new ordered list right after - // a paragraph, it should start with 1. - if (isTerminatingParagraph && markerValue !== 1) return false; - } else if ((posAfterMarker = skipBulletListMarker(state, startLine)) >= 0) { - isOrdered = false; - } else { - return false; - } - - // If we're starting a new unordered list right after - // a paragraph, first line should not be empty. - if (isTerminatingParagraph) { - if (state.skipSpaces(posAfterMarker) >= state.eMarks[startLine]) return false; - } - - // We should terminate list on style change. Remember first one to compare. - markerCharCode = state.src.charCodeAt(posAfterMarker - 1); - - // For validation mode we can terminate immediately - if (silent) { - return true; - } - - // Start list - listTokIdx = state.tokens.length; - if (isOrdered) { - token = state.push('ordered_list_open', 'ol', 1); - if (markerValue !== 1) { - token.attrs = [['start', markerValue]]; - } - } else { - token = state.push('bullet_list_open', 'ul', 1); - } - token.map = listLines = [startLine, 0]; - token.markup = String.fromCharCode(markerCharCode); - - // - // Iterate list items - // - - nextLine = startLine; - prevEmptyEnd = false; - terminatorRules = state.md.block.ruler.getRules('list'); - oldParentType = state.parentType; - state.parentType = 'list'; - while (nextLine < endLine) { - pos = posAfterMarker; - max = state.eMarks[nextLine]; - initial = offset = state.sCount[nextLine] + posAfterMarker - (state.bMarks[startLine] + state.tShift[startLine]); - while (pos < max) { - ch = state.src.charCodeAt(pos); - if (ch === 0x09) { - offset += 4 - (offset + state.bsCount[nextLine]) % 4; - } else if (ch === 0x20) { - offset++; - } else { - break; - } - pos++; - } - contentStart = pos; - if (contentStart >= max) { - // trimming space in "- \n 3" case, indent is 1 here - indentAfterMarker = 1; - } else { - indentAfterMarker = offset - initial; - } - - // If we have more than 4 spaces, the indent is 1 - // (the rest is just indented code block) - if (indentAfterMarker > 4) { - indentAfterMarker = 1; - } - - // " - test" - // ^^^^^ - calculating total length of this thing - indent = initial + indentAfterMarker; - - // Run subparser & write tokens - token = state.push('list_item_open', 'li', 1); - token.markup = String.fromCharCode(markerCharCode); - token.map = itemLines = [startLine, 0]; - if (isOrdered) { - token.info = state.src.slice(start, posAfterMarker - 1); - } - - // change current state, then restore it after parser subcall - oldTight = state.tight; - oldTShift = state.tShift[startLine]; - oldSCount = state.sCount[startLine]; - - // - example list - // ^ listIndent position will be here - // ^ blkIndent position will be here - // - oldListIndent = state.listIndent; - state.listIndent = state.blkIndent; - state.blkIndent = indent; - state.tight = true; - state.tShift[startLine] = contentStart - state.bMarks[startLine]; - state.sCount[startLine] = offset; - if (contentStart >= max && state.isEmpty(startLine + 1)) { - // workaround for this case - // (list item is empty, list terminates before "foo"): - // ~~~~~~~~ - // - - // - // foo - // ~~~~~~~~ - state.line = Math.min(state.line + 2, endLine); - } else { - state.md.block.tokenize(state, startLine, endLine, true); - } - - // If any of list item is tight, mark list as tight - if (!state.tight || prevEmptyEnd) { - tight = false; - } - // Item become loose if finish with empty line, - // but we should filter last element, because it means list finish - prevEmptyEnd = state.line - startLine > 1 && state.isEmpty(state.line - 1); - state.blkIndent = state.listIndent; - state.listIndent = oldListIndent; - state.tShift[startLine] = oldTShift; - state.sCount[startLine] = oldSCount; - state.tight = oldTight; - token = state.push('list_item_close', 'li', -1); - token.markup = String.fromCharCode(markerCharCode); - nextLine = startLine = state.line; - itemLines[1] = nextLine; - contentStart = state.bMarks[startLine]; - if (nextLine >= endLine) { - break; - } - - // - // Try to check if list is terminated or continued. - // - if (state.sCount[nextLine] < state.blkIndent) { - break; - } - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) { - break; - } - - // fail if terminating block found - terminate = false; - for (i = 0, l = terminatorRules.length; i < l; i++) { - if (terminatorRules[i](state, nextLine, endLine, true)) { - terminate = true; - break; - } - } - if (terminate) { - break; - } - - // fail if list has another type - if (isOrdered) { - posAfterMarker = skipOrderedListMarker(state, nextLine); - if (posAfterMarker < 0) { - break; - } - start = state.bMarks[nextLine] + state.tShift[nextLine]; - } else { - posAfterMarker = skipBulletListMarker(state, nextLine); - if (posAfterMarker < 0) { - break; - } - } - if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { - break; - } - } - - // Finalize list - if (isOrdered) { - token = state.push('ordered_list_close', 'ol', -1); - } else { - token = state.push('bullet_list_close', 'ul', -1); - } - token.markup = String.fromCharCode(markerCharCode); - listLines[1] = nextLine; - state.line = nextLine; - state.parentType = oldParentType; - - // mark paragraphs tight if needed - if (tight) { - markTightParagraphs(state, listTokIdx); - } - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/paragraph.js": -/*!**********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/paragraph.js ***! - \**********************************************************************/ -/***/ (function(module) { - -// Paragraph - - - -module.exports = function paragraph(state, startLine /*, endLine*/) { - var content, - terminate, - i, - l, - token, - oldParentType, - nextLine = startLine + 1, - terminatorRules = state.md.block.ruler.getRules('paragraph'), - endLine = state.lineMax; - oldParentType = state.parentType; - state.parentType = 'paragraph'; - - // jump line-by-line until empty one or EOF - for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { - // this would be a code block normally, but after paragraph - // it's considered a lazy continuation regardless of what's there - if (state.sCount[nextLine] - state.blkIndent > 3) { - continue; - } - - // quirk for blockquotes, this line should already be checked by that rule - if (state.sCount[nextLine] < 0) { - continue; - } - - // Some tags can terminate paragraph without empty line. - terminate = false; - for (i = 0, l = terminatorRules.length; i < l; i++) { - if (terminatorRules[i](state, nextLine, endLine, true)) { - terminate = true; - break; - } - } - if (terminate) { - break; - } - } - content = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); - state.line = nextLine; - token = state.push('paragraph_open', 'p', 1); - token.map = [startLine, state.line]; - token = state.push('inline', '', 0); - token.content = content; - token.map = [startLine, state.line]; - token.children = []; - token = state.push('paragraph_close', 'p', -1); - state.parentType = oldParentType; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/reference.js": -/*!**********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/reference.js ***! - \**********************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - - - -var normalizeReference = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").normalizeReference); -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -module.exports = function reference(state, startLine, _endLine, silent) { - var ch, - destEndPos, - destEndLineNo, - endLine, - href, - i, - l, - label, - labelEnd, - oldParentType, - res, - start, - str, - terminate, - terminatorRules, - title, - lines = 0, - pos = state.bMarks[startLine] + state.tShift[startLine], - max = state.eMarks[startLine], - nextLine = startLine + 1; - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[startLine] - state.blkIndent >= 4) { - return false; - } - if (state.src.charCodeAt(pos) !== 0x5B /* [ */) { - return false; - } - - // Simple check to quickly interrupt scan on [link](url) at the start of line. - // Can be useful on practice: https://github.com/markdown-it/markdown-it/issues/54 - while (++pos < max) { - if (state.src.charCodeAt(pos) === 0x5D /* ] */ && state.src.charCodeAt(pos - 1) !== 0x5C /* \ */) { - if (pos + 1 === max) { - return false; - } - if (state.src.charCodeAt(pos + 1) !== 0x3A /* : */) { - return false; - } - break; - } - } - endLine = state.lineMax; - - // jump line-by-line until empty one or EOF - terminatorRules = state.md.block.ruler.getRules('reference'); - oldParentType = state.parentType; - state.parentType = 'reference'; - for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { - // this would be a code block normally, but after paragraph - // it's considered a lazy continuation regardless of what's there - if (state.sCount[nextLine] - state.blkIndent > 3) { - continue; - } - - // quirk for blockquotes, this line should already be checked by that rule - if (state.sCount[nextLine] < 0) { - continue; - } - - // Some tags can terminate paragraph without empty line. - terminate = false; - for (i = 0, l = terminatorRules.length; i < l; i++) { - if (terminatorRules[i](state, nextLine, endLine, true)) { - terminate = true; - break; - } - } - if (terminate) { - break; - } - } - str = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); - max = str.length; - for (pos = 1; pos < max; pos++) { - ch = str.charCodeAt(pos); - if (ch === 0x5B /* [ */) { - return false; - } else if (ch === 0x5D /* ] */) { - labelEnd = pos; - break; - } else if (ch === 0x0A /* \n */) { - lines++; - } else if (ch === 0x5C /* \ */) { - pos++; - if (pos < max && str.charCodeAt(pos) === 0x0A) { - lines++; - } - } - } - if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A /* : */) { - return false; - } - - // [label]: destination 'title' - // ^^^ skip optional whitespace here - for (pos = labelEnd + 2; pos < max; pos++) { - ch = str.charCodeAt(pos); - if (ch === 0x0A) { - lines++; - } else if (isSpace(ch)) { - /*eslint no-empty:0*/ - } else { - break; - } - } - - // [label]: destination 'title' - // ^^^^^^^^^^^ parse this - res = state.md.helpers.parseLinkDestination(str, pos, max); - if (!res.ok) { - return false; - } - href = state.md.normalizeLink(res.str); - if (!state.md.validateLink(href)) { - return false; - } - pos = res.pos; - lines += res.lines; - - // save cursor state, we could require to rollback later - destEndPos = pos; - destEndLineNo = lines; - - // [label]: destination 'title' - // ^^^ skipping those spaces - start = pos; - for (; pos < max; pos++) { - ch = str.charCodeAt(pos); - if (ch === 0x0A) { - lines++; - } else if (isSpace(ch)) { - /*eslint no-empty:0*/ - } else { - break; - } - } - - // [label]: destination 'title' - // ^^^^^^^ parse this - res = state.md.helpers.parseLinkTitle(str, pos, max); - if (pos < max && start !== pos && res.ok) { - title = res.str; - pos = res.pos; - lines += res.lines; - } else { - title = ''; - pos = destEndPos; - lines = destEndLineNo; - } - - // skip trailing spaces until the rest of the line - while (pos < max) { - ch = str.charCodeAt(pos); - if (!isSpace(ch)) { - break; - } - pos++; - } - if (pos < max && str.charCodeAt(pos) !== 0x0A) { - if (title) { - // garbage at the end of the line after title, - // but it could still be a valid reference if we roll back - title = ''; - pos = destEndPos; - lines = destEndLineNo; - while (pos < max) { - ch = str.charCodeAt(pos); - if (!isSpace(ch)) { - break; - } - pos++; - } - } - } - if (pos < max && str.charCodeAt(pos) !== 0x0A) { - // garbage at the end of the line - return false; - } - label = normalizeReference(str.slice(1, labelEnd)); - if (!label) { - // CommonMark 0.20 disallows empty labels - return false; - } - - // Reference can not terminate anything. This check is for safety only. - /*istanbul ignore if*/ - if (silent) { - return true; - } - if (typeof state.env.references === 'undefined') { - state.env.references = {}; - } - if (typeof state.env.references[label] === 'undefined') { - state.env.references[label] = { - title: title, - href: href - }; - } - state.parentType = oldParentType; - state.line = startLine + lines + 1; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/state_block.js": -/*!************************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/state_block.js ***! - \************************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Parser state class - - - -var Token = __webpack_require__(/*! ../token */ "../../../node_modules/markdown-it/lib/token.js"); -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -function StateBlock(src, md, env, tokens) { - var ch, s, start, pos, len, indent, offset, indent_found; - this.src = src; - - // link to parser instance - this.md = md; - this.env = env; - - // - // Internal state vartiables - // - - this.tokens = tokens; - this.bMarks = []; // line begin offsets for fast jumps - this.eMarks = []; // line end offsets for fast jumps - this.tShift = []; // offsets of the first non-space characters (tabs not expanded) - this.sCount = []; // indents for each line (tabs expanded) - - // An amount of virtual spaces (tabs expanded) between beginning - // of each line (bMarks) and real beginning of that line. - // - // It exists only as a hack because blockquotes override bMarks - // losing information in the process. - // - // It's used only when expanding tabs, you can think about it as - // an initial tab length, e.g. bsCount=21 applied to string `\t123` - // means first tab should be expanded to 4-21%4 === 3 spaces. - // - this.bsCount = []; - - // block parser variables - this.blkIndent = 0; // required block content indent (for example, if we are - // inside a list, it would be positioned after list marker) - this.line = 0; // line index in src - this.lineMax = 0; // lines count - this.tight = false; // loose/tight mode for lists - this.ddIndent = -1; // indent of the current dd block (-1 if there isn't any) - this.listIndent = -1; // indent of the current list block (-1 if there isn't any) - - // can be 'blockquote', 'list', 'root', 'paragraph' or 'reference' - // used in lists to determine if they interrupt a paragraph - this.parentType = 'root'; - this.level = 0; - - // renderer - this.result = ''; - - // Create caches - // Generate markers. - s = this.src; - indent_found = false; - for (start = pos = indent = offset = 0, len = s.length; pos < len; pos++) { - ch = s.charCodeAt(pos); - if (!indent_found) { - if (isSpace(ch)) { - indent++; - if (ch === 0x09) { - offset += 4 - offset % 4; - } else { - offset++; - } - continue; - } else { - indent_found = true; - } - } - if (ch === 0x0A || pos === len - 1) { - if (ch !== 0x0A) { - pos++; - } - this.bMarks.push(start); - this.eMarks.push(pos); - this.tShift.push(indent); - this.sCount.push(offset); - this.bsCount.push(0); - indent_found = false; - indent = 0; - offset = 0; - start = pos + 1; - } - } - - // Push fake entry to simplify cache bounds checks - this.bMarks.push(s.length); - this.eMarks.push(s.length); - this.tShift.push(0); - this.sCount.push(0); - this.bsCount.push(0); - this.lineMax = this.bMarks.length - 1; // don't count last fake line -} - -// Push new token to "stream". -// -StateBlock.prototype.push = function (type, tag, nesting) { - var token = new Token(type, tag, nesting); - token.block = true; - if (nesting < 0) this.level--; // closing tag - token.level = this.level; - if (nesting > 0) this.level++; // opening tag - - this.tokens.push(token); - return token; -}; -StateBlock.prototype.isEmpty = function isEmpty(line) { - return this.bMarks[line] + this.tShift[line] >= this.eMarks[line]; -}; -StateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) { - for (var max = this.lineMax; from < max; from++) { - if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) { - break; - } - } - return from; -}; - -// Skip spaces from given position. -StateBlock.prototype.skipSpaces = function skipSpaces(pos) { - var ch; - for (var max = this.src.length; pos < max; pos++) { - ch = this.src.charCodeAt(pos); - if (!isSpace(ch)) { - break; - } - } - return pos; -}; - -// Skip spaces from given position in reverse. -StateBlock.prototype.skipSpacesBack = function skipSpacesBack(pos, min) { - if (pos <= min) { - return pos; - } - while (pos > min) { - if (!isSpace(this.src.charCodeAt(--pos))) { - return pos + 1; - } - } - return pos; -}; - -// Skip char codes from given position -StateBlock.prototype.skipChars = function skipChars(pos, code) { - for (var max = this.src.length; pos < max; pos++) { - if (this.src.charCodeAt(pos) !== code) { - break; - } - } - return pos; -}; - -// Skip char codes reverse from given position - 1 -StateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) { - if (pos <= min) { - return pos; - } - while (pos > min) { - if (code !== this.src.charCodeAt(--pos)) { - return pos + 1; - } - } - return pos; -}; - -// cut lines range from source. -StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) { - var i, - lineIndent, - ch, - first, - last, - queue, - lineStart, - line = begin; - if (begin >= end) { - return ''; - } - queue = new Array(end - begin); - for (i = 0; line < end; line++, i++) { - lineIndent = 0; - lineStart = first = this.bMarks[line]; - if (line + 1 < end || keepLastLF) { - // No need for bounds check because we have fake entry on tail. - last = this.eMarks[line] + 1; - } else { - last = this.eMarks[line]; - } - while (first < last && lineIndent < indent) { - ch = this.src.charCodeAt(first); - if (isSpace(ch)) { - if (ch === 0x09) { - lineIndent += 4 - (lineIndent + this.bsCount[line]) % 4; - } else { - lineIndent++; - } - } else if (first - lineStart < this.tShift[line]) { - // patched tShift masked characters to look like spaces (blockquotes, list markers) - lineIndent++; - } else { - break; - } - first++; - } - if (lineIndent > indent) { - // partially expanding tabs in code blocks, e.g '\t\tfoobar' - // with indent=2 becomes ' \tfoobar' - queue[i] = new Array(lineIndent - indent + 1).join(' ') + this.src.slice(first, last); - } else { - queue[i] = this.src.slice(first, last); - } - } - return queue.join(''); -}; - -// re-export Token class to use in block rules -StateBlock.prototype.Token = Token; -module.exports = StateBlock; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_block/table.js": -/*!******************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_block/table.js ***! - \******************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// GFM table, https://github.github.com/gfm/#tables-extension- - - - -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -function getLine(state, line) { - var pos = state.bMarks[line] + state.tShift[line], - max = state.eMarks[line]; - return state.src.substr(pos, max - pos); -} -function escapedSplit(str) { - var result = [], - pos = 0, - max = str.length, - ch, - isEscaped = false, - lastPos = 0, - current = ''; - ch = str.charCodeAt(pos); - while (pos < max) { - if (ch === 0x7c /* | */) { - if (!isEscaped) { - // pipe separating cells, '|' - result.push(current + str.substring(lastPos, pos)); - current = ''; - lastPos = pos + 1; - } else { - // escaped pipe, '\|' - current += str.substring(lastPos, pos - 1); - lastPos = pos; - } - } - isEscaped = ch === 0x5c /* \ */; - pos++; - ch = str.charCodeAt(pos); - } - result.push(current + str.substring(lastPos)); - return result; -} -module.exports = function table(state, startLine, endLine, silent) { - var ch, lineText, pos, i, l, nextLine, columns, columnCount, token, aligns, t, tableLines, tbodyLines, oldParentType, terminate, terminatorRules, firstCh, secondCh; - - // should have at least two lines - if (startLine + 2 > endLine) { - return false; - } - nextLine = startLine + 1; - if (state.sCount[nextLine] < state.blkIndent) { - return false; - } - - // if it's indented more than 3 spaces, it should be a code block - if (state.sCount[nextLine] - state.blkIndent >= 4) { - return false; - } - - // first character of the second line should be '|', '-', ':', - // and no other characters are allowed but spaces; - // basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp - - pos = state.bMarks[nextLine] + state.tShift[nextLine]; - if (pos >= state.eMarks[nextLine]) { - return false; - } - firstCh = state.src.charCodeAt(pos++); - if (firstCh !== 0x7C /* | */ && firstCh !== 0x2D /* - */ && firstCh !== 0x3A /* : */) { - return false; - } - if (pos >= state.eMarks[nextLine]) { - return false; - } - secondCh = state.src.charCodeAt(pos++); - if (secondCh !== 0x7C /* | */ && secondCh !== 0x2D /* - */ && secondCh !== 0x3A /* : */ && !isSpace(secondCh)) { - return false; - } - - // if first character is '-', then second character must not be a space - // (due to parsing ambiguity with list) - if (firstCh === 0x2D /* - */ && isSpace(secondCh)) { - return false; - } - while (pos < state.eMarks[nextLine]) { - ch = state.src.charCodeAt(pos); - if (ch !== 0x7C /* | */ && ch !== 0x2D /* - */ && ch !== 0x3A /* : */ && !isSpace(ch)) { - return false; - } - pos++; - } - lineText = getLine(state, startLine + 1); - columns = lineText.split('|'); - aligns = []; - for (i = 0; i < columns.length; i++) { - t = columns[i].trim(); - if (!t) { - // allow empty columns before and after table, but not in between columns; - // e.g. allow ` |---| `, disallow ` ---||--- ` - if (i === 0 || i === columns.length - 1) { - continue; - } else { - return false; - } - } - if (!/^:?-+:?$/.test(t)) { - return false; - } - if (t.charCodeAt(t.length - 1) === 0x3A /* : */) { - aligns.push(t.charCodeAt(0) === 0x3A /* : */ ? 'center' : 'right'); - } else if (t.charCodeAt(0) === 0x3A /* : */) { - aligns.push('left'); - } else { - aligns.push(''); - } - } - lineText = getLine(state, startLine).trim(); - if (lineText.indexOf('|') === -1) { - return false; - } - if (state.sCount[startLine] - state.blkIndent >= 4) { - return false; - } - columns = escapedSplit(lineText); - if (columns.length && columns[0] === '') columns.shift(); - if (columns.length && columns[columns.length - 1] === '') columns.pop(); - - // header row will define an amount of columns in the entire table, - // and align row should be exactly the same (the rest of the rows can differ) - columnCount = columns.length; - if (columnCount === 0 || columnCount !== aligns.length) { - return false; - } - if (silent) { - return true; - } - oldParentType = state.parentType; - state.parentType = 'table'; - - // use 'blockquote' lists for termination because it's - // the most similar to tables - terminatorRules = state.md.block.ruler.getRules('blockquote'); - token = state.push('table_open', 'table', 1); - token.map = tableLines = [startLine, 0]; - token = state.push('thead_open', 'thead', 1); - token.map = [startLine, startLine + 1]; - token = state.push('tr_open', 'tr', 1); - token.map = [startLine, startLine + 1]; - for (i = 0; i < columns.length; i++) { - token = state.push('th_open', 'th', 1); - if (aligns[i]) { - token.attrs = [['style', 'text-align:' + aligns[i]]]; - } - token = state.push('inline', '', 0); - token.content = columns[i].trim(); - token.children = []; - token = state.push('th_close', 'th', -1); - } - token = state.push('tr_close', 'tr', -1); - token = state.push('thead_close', 'thead', -1); - for (nextLine = startLine + 2; nextLine < endLine; nextLine++) { - if (state.sCount[nextLine] < state.blkIndent) { - break; - } - terminate = false; - for (i = 0, l = terminatorRules.length; i < l; i++) { - if (terminatorRules[i](state, nextLine, endLine, true)) { - terminate = true; - break; - } - } - if (terminate) { - break; - } - lineText = getLine(state, nextLine).trim(); - if (!lineText) { - break; - } - if (state.sCount[nextLine] - state.blkIndent >= 4) { - break; - } - columns = escapedSplit(lineText); - if (columns.length && columns[0] === '') columns.shift(); - if (columns.length && columns[columns.length - 1] === '') columns.pop(); - if (nextLine === startLine + 2) { - token = state.push('tbody_open', 'tbody', 1); - token.map = tbodyLines = [startLine + 2, 0]; - } - token = state.push('tr_open', 'tr', 1); - token.map = [nextLine, nextLine + 1]; - for (i = 0; i < columnCount; i++) { - token = state.push('td_open', 'td', 1); - if (aligns[i]) { - token.attrs = [['style', 'text-align:' + aligns[i]]]; - } - token = state.push('inline', '', 0); - token.content = columns[i] ? columns[i].trim() : ''; - token.children = []; - token = state.push('td_close', 'td', -1); - } - token = state.push('tr_close', 'tr', -1); - } - if (tbodyLines) { - token = state.push('tbody_close', 'tbody', -1); - tbodyLines[1] = nextLine; - } - token = state.push('table_close', 'table', -1); - tableLines[1] = nextLine; - state.parentType = oldParentType; - state.line = nextLine; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_core/block.js": -/*!*****************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_core/block.js ***! - \*****************************************************************/ -/***/ (function(module) { - - - -module.exports = function block(state) { - var token; - if (state.inlineMode) { - token = new state.Token('inline', '', 0); - token.content = state.src; - token.map = [0, 1]; - token.children = []; - state.tokens.push(token); - } else { - state.md.block.parse(state.src, state.md, state.env, state.tokens); - } -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_core/inline.js": -/*!******************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_core/inline.js ***! - \******************************************************************/ -/***/ (function(module) { - - - -module.exports = function inline(state) { - var tokens = state.tokens, - tok, - i, - l; - - // Parse inlines - for (i = 0, l = tokens.length; i < l; i++) { - tok = tokens[i]; - if (tok.type === 'inline') { - state.md.inline.parse(tok.content, state.md, state.env, tok.children); - } - } -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_core/linkify.js": -/*!*******************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_core/linkify.js ***! - \*******************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Replace link-like texts with link nodes. -// -// Currently restricted by `md.validateLink()` to http/https/ftp -// - - -var arrayReplaceAt = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").arrayReplaceAt); -function isLinkOpen(str) { - return /^\s]/i.test(str); -} -function isLinkClose(str) { - return /^<\/a\s*>/i.test(str); -} -module.exports = function linkify(state) { - var i, - j, - l, - tokens, - token, - currentToken, - nodes, - ln, - text, - pos, - lastPos, - level, - htmlLinkLevel, - url, - fullUrl, - urlText, - blockTokens = state.tokens, - links; - if (!state.md.options.linkify) { - return; - } - for (j = 0, l = blockTokens.length; j < l; j++) { - if (blockTokens[j].type !== 'inline' || !state.md.linkify.pretest(blockTokens[j].content)) { - continue; - } - tokens = blockTokens[j].children; - htmlLinkLevel = 0; - - // We scan from the end, to keep position when new tags added. - // Use reversed logic in links start/end match - for (i = tokens.length - 1; i >= 0; i--) { - currentToken = tokens[i]; - - // Skip content of markdown links - if (currentToken.type === 'link_close') { - i--; - while (tokens[i].level !== currentToken.level && tokens[i].type !== 'link_open') { - i--; - } - continue; - } - - // Skip content of html tag links - if (currentToken.type === 'html_inline') { - if (isLinkOpen(currentToken.content) && htmlLinkLevel > 0) { - htmlLinkLevel--; - } - if (isLinkClose(currentToken.content)) { - htmlLinkLevel++; - } - } - if (htmlLinkLevel > 0) { - continue; - } - if (currentToken.type === 'text' && state.md.linkify.test(currentToken.content)) { - text = currentToken.content; - links = state.md.linkify.match(text); - - // Now split string to nodes - nodes = []; - level = currentToken.level; - lastPos = 0; - for (ln = 0; ln < links.length; ln++) { - url = links[ln].url; - fullUrl = state.md.normalizeLink(url); - if (!state.md.validateLink(fullUrl)) { - continue; - } - urlText = links[ln].text; - - // Linkifier might send raw hostnames like "example.com", where url - // starts with domain name. So we prepend http:// in those cases, - // and remove it afterwards. - // - if (!links[ln].schema) { - urlText = state.md.normalizeLinkText('http://' + urlText).replace(/^http:\/\//, ''); - } else if (links[ln].schema === 'mailto:' && !/^mailto:/i.test(urlText)) { - urlText = state.md.normalizeLinkText('mailto:' + urlText).replace(/^mailto:/, ''); - } else { - urlText = state.md.normalizeLinkText(urlText); - } - pos = links[ln].index; - if (pos > lastPos) { - token = new state.Token('text', '', 0); - token.content = text.slice(lastPos, pos); - token.level = level; - nodes.push(token); - } - token = new state.Token('link_open', 'a', 1); - token.attrs = [['href', fullUrl]]; - token.level = level++; - token.markup = 'linkify'; - token.info = 'auto'; - nodes.push(token); - token = new state.Token('text', '', 0); - token.content = urlText; - token.level = level; - nodes.push(token); - token = new state.Token('link_close', 'a', -1); - token.level = --level; - token.markup = 'linkify'; - token.info = 'auto'; - nodes.push(token); - lastPos = links[ln].lastIndex; - } - if (lastPos < text.length) { - token = new state.Token('text', '', 0); - token.content = text.slice(lastPos); - token.level = level; - nodes.push(token); - } - - // replace current node - blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes); - } - } - } -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_core/normalize.js": -/*!*********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_core/normalize.js ***! - \*********************************************************************/ -/***/ (function(module) { - -// Normalize input string - - - -// https://spec.commonmark.org/0.29/#line-ending -var NEWLINES_RE = /\r\n?|\n/g; -var NULL_RE = /\0/g; -module.exports = function normalize(state) { - var str; - - // Normalize newlines - str = state.src.replace(NEWLINES_RE, '\n'); - - // Replace NULL characters - str = str.replace(NULL_RE, '\uFFFD'); - state.src = str; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_core/replacements.js": -/*!************************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_core/replacements.js ***! - \************************************************************************/ -/***/ (function(module) { - -// Simple typographic replacements -// -// (c) (C) → © -// (tm) (TM) → ™ -// (r) (R) → ® -// +- → ± -// (p) (P) -> § -// ... → … (also ?.... → ?.., !.... → !..) -// ???????? → ???, !!!!! → !!!, `,,` → `,` -// -- → –, --- → — -// - - -// TODO: -// - fractionals 1/2, 1/4, 3/4 -> ½, ¼, ¾ -// - miltiplication 2 x 4 -> 2 × 4 -var RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--/; - -// Workaround for phantomjs - need regex without /g flag, -// or root check will fail every second time -var SCOPED_ABBR_TEST_RE = /\((c|tm|r|p)\)/i; -var SCOPED_ABBR_RE = /\((c|tm|r|p)\)/ig; -var SCOPED_ABBR = { - c: '©', - r: '®', - p: '§', - tm: '™' -}; -function replaceFn(match, name) { - return SCOPED_ABBR[name.toLowerCase()]; -} -function replace_scoped(inlineTokens) { - var i, - token, - inside_autolink = 0; - for (i = inlineTokens.length - 1; i >= 0; i--) { - token = inlineTokens[i]; - if (token.type === 'text' && !inside_autolink) { - token.content = token.content.replace(SCOPED_ABBR_RE, replaceFn); - } - if (token.type === 'link_open' && token.info === 'auto') { - inside_autolink--; - } - if (token.type === 'link_close' && token.info === 'auto') { - inside_autolink++; - } - } -} -function replace_rare(inlineTokens) { - var i, - token, - inside_autolink = 0; - for (i = inlineTokens.length - 1; i >= 0; i--) { - token = inlineTokens[i]; - if (token.type === 'text' && !inside_autolink) { - if (RARE_RE.test(token.content)) { - token.content = token.content.replace(/\+-/g, '±') - // .., ..., ....... -> … - // but ?..... & !..... -> ?.. & !.. - .replace(/\.{2,}/g, '…').replace(/([?!])…/g, '$1..').replace(/([?!]){4,}/g, '$1$1$1').replace(/,{2,}/g, ',') - // em-dash - .replace(/(^|[^-])---(?=[^-]|$)/mg, '$1\u2014') - // en-dash - .replace(/(^|\s)--(?=\s|$)/mg, '$1\u2013').replace(/(^|[^-\s])--(?=[^-\s]|$)/mg, '$1\u2013'); - } - } - if (token.type === 'link_open' && token.info === 'auto') { - inside_autolink--; - } - if (token.type === 'link_close' && token.info === 'auto') { - inside_autolink++; - } - } -} -module.exports = function replace(state) { - var blkIdx; - if (!state.md.options.typographer) { - return; - } - for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { - if (state.tokens[blkIdx].type !== 'inline') { - continue; - } - if (SCOPED_ABBR_TEST_RE.test(state.tokens[blkIdx].content)) { - replace_scoped(state.tokens[blkIdx].children); - } - if (RARE_RE.test(state.tokens[blkIdx].content)) { - replace_rare(state.tokens[blkIdx].children); - } - } -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_core/smartquotes.js": -/*!***********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_core/smartquotes.js ***! - \***********************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Convert straight quotation marks to typographic ones -// - - -var isWhiteSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isWhiteSpace); -var isPunctChar = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isPunctChar); -var isMdAsciiPunct = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isMdAsciiPunct); -var QUOTE_TEST_RE = /['"]/; -var QUOTE_RE = /['"]/g; -var APOSTROPHE = '\u2019'; /* ’ */ - -function replaceAt(str, index, ch) { - return str.substr(0, index) + ch + str.substr(index + 1); -} -function process_inlines(tokens, state) { - var i, token, text, t, pos, max, thisLevel, item, lastChar, nextChar, isLastPunctChar, isNextPunctChar, isLastWhiteSpace, isNextWhiteSpace, canOpen, canClose, j, isSingle, stack, openQuote, closeQuote; - stack = []; - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - thisLevel = tokens[i].level; - for (j = stack.length - 1; j >= 0; j--) { - if (stack[j].level <= thisLevel) { - break; - } - } - stack.length = j + 1; - if (token.type !== 'text') { - continue; - } - text = token.content; - pos = 0; - max = text.length; - - /*eslint no-labels:0,block-scoped-var:0*/ - OUTER: while (pos < max) { - QUOTE_RE.lastIndex = pos; - t = QUOTE_RE.exec(text); - if (!t) { - break; - } - canOpen = canClose = true; - pos = t.index + 1; - isSingle = t[0] === "'"; - - // Find previous character, - // default to space if it's the beginning of the line - // - lastChar = 0x20; - if (t.index - 1 >= 0) { - lastChar = text.charCodeAt(t.index - 1); - } else { - for (j = i - 1; j >= 0; j--) { - if (tokens[j].type === 'softbreak' || tokens[j].type === 'hardbreak') break; // lastChar defaults to 0x20 - if (!tokens[j].content) continue; // should skip all tokens except 'text', 'html_inline' or 'code_inline' - - lastChar = tokens[j].content.charCodeAt(tokens[j].content.length - 1); - break; - } - } - - // Find next character, - // default to space if it's the end of the line - // - nextChar = 0x20; - if (pos < max) { - nextChar = text.charCodeAt(pos); - } else { - for (j = i + 1; j < tokens.length; j++) { - if (tokens[j].type === 'softbreak' || tokens[j].type === 'hardbreak') break; // nextChar defaults to 0x20 - if (!tokens[j].content) continue; // should skip all tokens except 'text', 'html_inline' or 'code_inline' - - nextChar = tokens[j].content.charCodeAt(0); - break; - } - } - isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar)); - isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar)); - isLastWhiteSpace = isWhiteSpace(lastChar); - isNextWhiteSpace = isWhiteSpace(nextChar); - if (isNextWhiteSpace) { - canOpen = false; - } else if (isNextPunctChar) { - if (!(isLastWhiteSpace || isLastPunctChar)) { - canOpen = false; - } - } - if (isLastWhiteSpace) { - canClose = false; - } else if (isLastPunctChar) { - if (!(isNextWhiteSpace || isNextPunctChar)) { - canClose = false; - } - } - if (nextChar === 0x22 /* " */ && t[0] === '"') { - if (lastChar >= 0x30 /* 0 */ && lastChar <= 0x39 /* 9 */) { - // special case: 1"" - count first quote as an inch - canClose = canOpen = false; - } - } - if (canOpen && canClose) { - // Replace quotes in the middle of punctuation sequence, but not - // in the middle of the words, i.e.: - // - // 1. foo " bar " baz - not replaced - // 2. foo-"-bar-"-baz - replaced - // 3. foo"bar"baz - not replaced - // - canOpen = isLastPunctChar; - canClose = isNextPunctChar; - } - if (!canOpen && !canClose) { - // middle of word - if (isSingle) { - token.content = replaceAt(token.content, t.index, APOSTROPHE); - } - continue; - } - if (canClose) { - // this could be a closing quote, rewind the stack to get a match - for (j = stack.length - 1; j >= 0; j--) { - item = stack[j]; - if (stack[j].level < thisLevel) { - break; - } - if (item.single === isSingle && stack[j].level === thisLevel) { - item = stack[j]; - if (isSingle) { - openQuote = state.md.options.quotes[2]; - closeQuote = state.md.options.quotes[3]; - } else { - openQuote = state.md.options.quotes[0]; - closeQuote = state.md.options.quotes[1]; - } - - // replace token.content *before* tokens[item.token].content, - // because, if they are pointing at the same token, replaceAt - // could mess up indices when quote length != 1 - token.content = replaceAt(token.content, t.index, closeQuote); - tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, openQuote); - pos += closeQuote.length - 1; - if (item.token === i) { - pos += openQuote.length - 1; - } - text = token.content; - max = text.length; - stack.length = j; - continue OUTER; - } - } - } - if (canOpen) { - stack.push({ - token: i, - pos: t.index, - single: isSingle, - level: thisLevel - }); - } else if (canClose && isSingle) { - token.content = replaceAt(token.content, t.index, APOSTROPHE); - } - } - } -} -module.exports = function smartquotes(state) { - /*eslint max-depth:0*/ - var blkIdx; - if (!state.md.options.typographer) { - return; - } - for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { - if (state.tokens[blkIdx].type !== 'inline' || !QUOTE_TEST_RE.test(state.tokens[blkIdx].content)) { - continue; - } - process_inlines(state.tokens[blkIdx].children, state); - } -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_core/state_core.js": -/*!**********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_core/state_core.js ***! - \**********************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Core state object -// - - -var Token = __webpack_require__(/*! ../token */ "../../../node_modules/markdown-it/lib/token.js"); -function StateCore(src, md, env) { - this.src = src; - this.env = env; - this.tokens = []; - this.inlineMode = false; - this.md = md; // link to parser instance -} - -// re-export Token class to use in core rules -StateCore.prototype.Token = Token; -module.exports = StateCore; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/autolink.js": -/*!**********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/autolink.js ***! - \**********************************************************************/ -/***/ (function(module) { - -// Process autolinks '' - - - -/*eslint max-len:0*/ -var EMAIL_RE = /^([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/; -var AUTOLINK_RE = /^([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)$/; -module.exports = function autolink(state, silent) { - var url, - fullUrl, - token, - ch, - start, - max, - pos = state.pos; - if (state.src.charCodeAt(pos) !== 0x3C /* < */) { - return false; - } - start = state.pos; - max = state.posMax; - for (;;) { - if (++pos >= max) return false; - ch = state.src.charCodeAt(pos); - if (ch === 0x3C /* < */) return false; - if (ch === 0x3E /* > */) break; - } - url = state.src.slice(start + 1, pos); - if (AUTOLINK_RE.test(url)) { - fullUrl = state.md.normalizeLink(url); - if (!state.md.validateLink(fullUrl)) { - return false; - } - if (!silent) { - token = state.push('link_open', 'a', 1); - token.attrs = [['href', fullUrl]]; - token.markup = 'autolink'; - token.info = 'auto'; - token = state.push('text', '', 0); - token.content = state.md.normalizeLinkText(url); - token = state.push('link_close', 'a', -1); - token.markup = 'autolink'; - token.info = 'auto'; - } - state.pos += url.length + 2; - return true; - } - if (EMAIL_RE.test(url)) { - fullUrl = state.md.normalizeLink('mailto:' + url); - if (!state.md.validateLink(fullUrl)) { - return false; - } - if (!silent) { - token = state.push('link_open', 'a', 1); - token.attrs = [['href', fullUrl]]; - token.markup = 'autolink'; - token.info = 'auto'; - token = state.push('text', '', 0); - token.content = state.md.normalizeLinkText(url); - token = state.push('link_close', 'a', -1); - token.markup = 'autolink'; - token.info = 'auto'; - } - state.pos += url.length + 2; - return true; - } - return false; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/backticks.js": -/*!***********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/backticks.js ***! - \***********************************************************************/ -/***/ (function(module) { - -// Parse backticks - - - -module.exports = function backtick(state, silent) { - var start, - max, - marker, - token, - matchStart, - matchEnd, - openerLength, - closerLength, - pos = state.pos, - ch = state.src.charCodeAt(pos); - if (ch !== 0x60 /* ` */) { - return false; - } - start = pos; - pos++; - max = state.posMax; - - // scan marker length - while (pos < max && state.src.charCodeAt(pos) === 0x60 /* ` */) { - pos++; - } - marker = state.src.slice(start, pos); - openerLength = marker.length; - if (state.backticksScanned && (state.backticks[openerLength] || 0) <= start) { - if (!silent) state.pending += marker; - state.pos += openerLength; - return true; - } - matchStart = matchEnd = pos; - - // Nothing found in the cache, scan until the end of the line (or until marker is found) - while ((matchStart = state.src.indexOf('`', matchEnd)) !== -1) { - matchEnd = matchStart + 1; - - // scan marker length - while (matchEnd < max && state.src.charCodeAt(matchEnd) === 0x60 /* ` */) { - matchEnd++; - } - closerLength = matchEnd - matchStart; - if (closerLength === openerLength) { - // Found matching closer length. - if (!silent) { - token = state.push('code_inline', 'code', 0); - token.markup = marker; - token.content = state.src.slice(pos, matchStart).replace(/\n/g, ' ').replace(/^ (.+) $/, '$1'); - } - state.pos = matchEnd; - return true; - } - - // Some different length found, put it in cache as upper limit of where closer can be found - state.backticks[closerLength] = matchStart; - } - - // Scanned through the end, didn't find anything - state.backticksScanned = true; - if (!silent) state.pending += marker; - state.pos += openerLength; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/balance_pairs.js": -/*!***************************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/balance_pairs.js ***! - \***************************************************************************/ -/***/ (function(module) { - -// For each opening emphasis-like marker find a matching closing one -// - - -function processDelimiters(state, delimiters) { - var closerIdx, - openerIdx, - closer, - opener, - minOpenerIdx, - newMinOpenerIdx, - isOddMatch, - lastJump, - openersBottom = {}, - max = delimiters.length; - for (closerIdx = 0; closerIdx < max; closerIdx++) { - closer = delimiters[closerIdx]; - - // Length is only used for emphasis-specific "rule of 3", - // if it's not defined (in strikethrough or 3rd party plugins), - // we can default it to 0 to disable those checks. - // - closer.length = closer.length || 0; - if (!closer.close) continue; - - // Previously calculated lower bounds (previous fails) - // for each marker, each delimiter length modulo 3, - // and for whether this closer can be an opener; - // https://github.com/commonmark/cmark/commit/34250e12ccebdc6372b8b49c44fab57c72443460 - if (!openersBottom.hasOwnProperty(closer.marker)) { - openersBottom[closer.marker] = [-1, -1, -1, -1, -1, -1]; - } - minOpenerIdx = openersBottom[closer.marker][(closer.open ? 3 : 0) + closer.length % 3]; - openerIdx = closerIdx - closer.jump - 1; - - // avoid crash if `closer.jump` is pointing outside of the array, see #742 - if (openerIdx < -1) openerIdx = -1; - newMinOpenerIdx = openerIdx; - for (; openerIdx > minOpenerIdx; openerIdx -= opener.jump + 1) { - opener = delimiters[openerIdx]; - if (opener.marker !== closer.marker) continue; - if (opener.open && opener.end < 0) { - isOddMatch = false; - - // from spec: - // - // If one of the delimiters can both open and close emphasis, then the - // sum of the lengths of the delimiter runs containing the opening and - // closing delimiters must not be a multiple of 3 unless both lengths - // are multiples of 3. - // - if (opener.close || closer.open) { - if ((opener.length + closer.length) % 3 === 0) { - if (opener.length % 3 !== 0 || closer.length % 3 !== 0) { - isOddMatch = true; - } - } - } - if (!isOddMatch) { - // If previous delimiter cannot be an opener, we can safely skip - // the entire sequence in future checks. This is required to make - // sure algorithm has linear complexity (see *_*_*_*_*_... case). - // - lastJump = openerIdx > 0 && !delimiters[openerIdx - 1].open ? delimiters[openerIdx - 1].jump + 1 : 0; - closer.jump = closerIdx - openerIdx + lastJump; - closer.open = false; - opener.end = closerIdx; - opener.jump = lastJump; - opener.close = false; - newMinOpenerIdx = -1; - break; - } - } - } - if (newMinOpenerIdx !== -1) { - // If match for this delimiter run failed, we want to set lower bound for - // future lookups. This is required to make sure algorithm has linear - // complexity. - // - // See details here: - // https://github.com/commonmark/cmark/issues/178#issuecomment-270417442 - // - openersBottom[closer.marker][(closer.open ? 3 : 0) + (closer.length || 0) % 3] = newMinOpenerIdx; - } - } -} -module.exports = function link_pairs(state) { - var curr, - tokens_meta = state.tokens_meta, - max = state.tokens_meta.length; - processDelimiters(state, state.delimiters); - for (curr = 0; curr < max; curr++) { - if (tokens_meta[curr] && tokens_meta[curr].delimiters) { - processDelimiters(state, tokens_meta[curr].delimiters); - } - } -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/emphasis.js": -/*!**********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/emphasis.js ***! - \**********************************************************************/ -/***/ (function(module) { - -// Process *this* and _that_ -// - - -// Insert each marker as a separate text token, and add it to delimiter list -// -module.exports.tokenize = function emphasis(state, silent) { - var i, - scanned, - token, - start = state.pos, - marker = state.src.charCodeAt(start); - if (silent) { - return false; - } - if (marker !== 0x5F /* _ */ && marker !== 0x2A /* * */) { - return false; - } - scanned = state.scanDelims(state.pos, marker === 0x2A); - for (i = 0; i < scanned.length; i++) { - token = state.push('text', '', 0); - token.content = String.fromCharCode(marker); - state.delimiters.push({ - // Char code of the starting marker (number). - // - marker: marker, - // Total length of these series of delimiters. - // - length: scanned.length, - // An amount of characters before this one that's equivalent to - // current one. In plain English: if this delimiter does not open - // an emphasis, neither do previous `jump` characters. - // - // Used to skip sequences like "*****" in one step, for 1st asterisk - // value will be 0, for 2nd it's 1 and so on. - // - jump: i, - // A position of the token this delimiter corresponds to. - // - token: state.tokens.length - 1, - // If this delimiter is matched as a valid opener, `end` will be - // equal to its position, otherwise it's `-1`. - // - end: -1, - // Boolean flags that determine if this delimiter could open or close - // an emphasis. - // - open: scanned.can_open, - close: scanned.can_close - }); - } - state.pos += scanned.length; - return true; -}; -function postProcess(state, delimiters) { - var i, - startDelim, - endDelim, - token, - ch, - isStrong, - max = delimiters.length; - for (i = max - 1; i >= 0; i--) { - startDelim = delimiters[i]; - if (startDelim.marker !== 0x5F /* _ */ && startDelim.marker !== 0x2A /* * */) { - continue; - } - - // Process only opening markers - if (startDelim.end === -1) { - continue; - } - endDelim = delimiters[startDelim.end]; - - // If the previous delimiter has the same marker and is adjacent to this one, - // merge those into one strong delimiter. - // - // `whatever` -> `whatever` - // - isStrong = i > 0 && delimiters[i - 1].end === startDelim.end + 1 && delimiters[i - 1].token === startDelim.token - 1 && delimiters[startDelim.end + 1].token === endDelim.token + 1 && delimiters[i - 1].marker === startDelim.marker; - ch = String.fromCharCode(startDelim.marker); - token = state.tokens[startDelim.token]; - token.type = isStrong ? 'strong_open' : 'em_open'; - token.tag = isStrong ? 'strong' : 'em'; - token.nesting = 1; - token.markup = isStrong ? ch + ch : ch; - token.content = ''; - token = state.tokens[endDelim.token]; - token.type = isStrong ? 'strong_close' : 'em_close'; - token.tag = isStrong ? 'strong' : 'em'; - token.nesting = -1; - token.markup = isStrong ? ch + ch : ch; - token.content = ''; - if (isStrong) { - state.tokens[delimiters[i - 1].token].content = ''; - state.tokens[delimiters[startDelim.end + 1].token].content = ''; - i--; - } - } -} - -// Walk through delimiter list and replace text tokens with tags -// -module.exports.postProcess = function emphasis(state) { - var curr, - tokens_meta = state.tokens_meta, - max = state.tokens_meta.length; - postProcess(state, state.delimiters); - for (curr = 0; curr < max; curr++) { - if (tokens_meta[curr] && tokens_meta[curr].delimiters) { - postProcess(state, tokens_meta[curr].delimiters); - } - } -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/entity.js": -/*!********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/entity.js ***! - \********************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Process html entity - {, ¯, ", ... - - - -var entities = __webpack_require__(/*! ../common/entities */ "../../../node_modules/markdown-it/lib/common/entities.js"); -var has = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").has); -var isValidEntityCode = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isValidEntityCode); -var fromCodePoint = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").fromCodePoint); -var DIGITAL_RE = /^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i; -var NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i; -module.exports = function entity(state, silent) { - var ch, - code, - match, - pos = state.pos, - max = state.posMax; - if (state.src.charCodeAt(pos) !== 0x26 /* & */) { - return false; - } - if (pos + 1 < max) { - ch = state.src.charCodeAt(pos + 1); - if (ch === 0x23 /* # */) { - match = state.src.slice(pos).match(DIGITAL_RE); - if (match) { - if (!silent) { - code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10); - state.pending += isValidEntityCode(code) ? fromCodePoint(code) : fromCodePoint(0xFFFD); - } - state.pos += match[0].length; - return true; - } - } else { - match = state.src.slice(pos).match(NAMED_RE); - if (match) { - if (has(entities, match[1])) { - if (!silent) { - state.pending += entities[match[1]]; - } - state.pos += match[0].length; - return true; - } - } - } - } - if (!silent) { - state.pending += '&'; - } - state.pos++; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/escape.js": -/*!********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/escape.js ***! - \********************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Process escaped chars and hardbreaks - - - -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -var ESCAPED = []; -for (var i = 0; i < 256; i++) { - ESCAPED.push(0); -} -'\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-'.split('').forEach(function (ch) { - ESCAPED[ch.charCodeAt(0)] = 1; -}); -module.exports = function escape(state, silent) { - var ch, - pos = state.pos, - max = state.posMax; - if (state.src.charCodeAt(pos) !== 0x5C /* \ */) { - return false; - } - pos++; - if (pos < max) { - ch = state.src.charCodeAt(pos); - if (ch < 256 && ESCAPED[ch] !== 0) { - if (!silent) { - state.pending += state.src[pos]; - } - state.pos += 2; - return true; - } - if (ch === 0x0A) { - if (!silent) { - state.push('hardbreak', 'br', 0); - } - pos++; - // skip leading whitespaces from next line - while (pos < max) { - ch = state.src.charCodeAt(pos); - if (!isSpace(ch)) { - break; - } - pos++; - } - state.pos = pos; - return true; - } - } - if (!silent) { - state.pending += '\\'; - } - state.pos++; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/html_inline.js": -/*!*************************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/html_inline.js ***! - \*************************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Process html tags - - - -var HTML_TAG_RE = (__webpack_require__(/*! ../common/html_re */ "../../../node_modules/markdown-it/lib/common/html_re.js").HTML_TAG_RE); -function isLetter(ch) { - /*eslint no-bitwise:0*/ - var lc = ch | 0x20; // to lower case - return lc >= 0x61 /* a */ && lc <= 0x7a /* z */; -} - -module.exports = function html_inline(state, silent) { - var ch, - match, - max, - token, - pos = state.pos; - if (!state.md.options.html) { - return false; - } - - // Check start - max = state.posMax; - if (state.src.charCodeAt(pos) !== 0x3C /* < */ || pos + 2 >= max) { - return false; - } - - // Quick fail on second char - ch = state.src.charCodeAt(pos + 1); - if (ch !== 0x21 /* ! */ && ch !== 0x3F /* ? */ && ch !== 0x2F /* / */ && !isLetter(ch)) { - return false; - } - match = state.src.slice(pos).match(HTML_TAG_RE); - if (!match) { - return false; - } - if (!silent) { - token = state.push('html_inline', '', 0); - token.content = state.src.slice(pos, pos + match[0].length); - } - state.pos += match[0].length; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/image.js": -/*!*******************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/image.js ***! - \*******************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Process ![image]( "title") - - - -var normalizeReference = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").normalizeReference); -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -module.exports = function image(state, silent) { - var attrs, - code, - content, - label, - labelEnd, - labelStart, - pos, - ref, - res, - title, - token, - tokens, - start, - href = '', - oldPos = state.pos, - max = state.posMax; - if (state.src.charCodeAt(state.pos) !== 0x21 /* ! */) { - return false; - } - if (state.src.charCodeAt(state.pos + 1) !== 0x5B /* [ */) { - return false; - } - labelStart = state.pos + 2; - labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false); - - // parser failed to find ']', so it's not a valid link - if (labelEnd < 0) { - return false; - } - pos = labelEnd + 1; - if (pos < max && state.src.charCodeAt(pos) === 0x28 /* ( */) { - // - // Inline link - // - - // [link]( "title" ) - // ^^ skipping these spaces - pos++; - for (; pos < max; pos++) { - code = state.src.charCodeAt(pos); - if (!isSpace(code) && code !== 0x0A) { - break; - } - } - if (pos >= max) { - return false; - } - - // [link]( "title" ) - // ^^^^^^ parsing link destination - start = pos; - res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax); - if (res.ok) { - href = state.md.normalizeLink(res.str); - if (state.md.validateLink(href)) { - pos = res.pos; - } else { - href = ''; - } - } - - // [link]( "title" ) - // ^^ skipping these spaces - start = pos; - for (; pos < max; pos++) { - code = state.src.charCodeAt(pos); - if (!isSpace(code) && code !== 0x0A) { - break; - } - } - - // [link]( "title" ) - // ^^^^^^^ parsing link title - res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax); - if (pos < max && start !== pos && res.ok) { - title = res.str; - pos = res.pos; - - // [link]( "title" ) - // ^^ skipping these spaces - for (; pos < max; pos++) { - code = state.src.charCodeAt(pos); - if (!isSpace(code) && code !== 0x0A) { - break; - } - } - } else { - title = ''; - } - if (pos >= max || state.src.charCodeAt(pos) !== 0x29 /* ) */) { - state.pos = oldPos; - return false; - } - pos++; - } else { - // - // Link reference - // - if (typeof state.env.references === 'undefined') { - return false; - } - if (pos < max && state.src.charCodeAt(pos) === 0x5B /* [ */) { - start = pos + 1; - pos = state.md.helpers.parseLinkLabel(state, pos); - if (pos >= 0) { - label = state.src.slice(start, pos++); - } else { - pos = labelEnd + 1; - } - } else { - pos = labelEnd + 1; - } - - // covers label === '' and label === undefined - // (collapsed reference link and shortcut reference link respectively) - if (!label) { - label = state.src.slice(labelStart, labelEnd); - } - ref = state.env.references[normalizeReference(label)]; - if (!ref) { - state.pos = oldPos; - return false; - } - href = ref.href; - title = ref.title; - } - - // - // We found the end of the link, and know for a fact it's a valid link; - // so all that's left to do is to call tokenizer. - // - if (!silent) { - content = state.src.slice(labelStart, labelEnd); - state.md.inline.parse(content, state.md, state.env, tokens = []); - token = state.push('image', 'img', 0); - token.attrs = attrs = [['src', href], ['alt', '']]; - token.children = tokens; - token.content = content; - if (title) { - attrs.push(['title', title]); - } - } - state.pos = pos; - state.posMax = max; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/link.js": -/*!******************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/link.js ***! - \******************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Process [link]( "stuff") - - - -var normalizeReference = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").normalizeReference); -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -module.exports = function link(state, silent) { - var attrs, - code, - label, - labelEnd, - labelStart, - pos, - res, - ref, - token, - href = '', - title = '', - oldPos = state.pos, - max = state.posMax, - start = state.pos, - parseReference = true; - if (state.src.charCodeAt(state.pos) !== 0x5B /* [ */) { - return false; - } - labelStart = state.pos + 1; - labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true); - - // parser failed to find ']', so it's not a valid link - if (labelEnd < 0) { - return false; - } - pos = labelEnd + 1; - if (pos < max && state.src.charCodeAt(pos) === 0x28 /* ( */) { - // - // Inline link - // - - // might have found a valid shortcut link, disable reference parsing - parseReference = false; - - // [link]( "title" ) - // ^^ skipping these spaces - pos++; - for (; pos < max; pos++) { - code = state.src.charCodeAt(pos); - if (!isSpace(code) && code !== 0x0A) { - break; - } - } - if (pos >= max) { - return false; - } - - // [link]( "title" ) - // ^^^^^^ parsing link destination - start = pos; - res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax); - if (res.ok) { - href = state.md.normalizeLink(res.str); - if (state.md.validateLink(href)) { - pos = res.pos; - } else { - href = ''; - } - - // [link]( "title" ) - // ^^ skipping these spaces - start = pos; - for (; pos < max; pos++) { - code = state.src.charCodeAt(pos); - if (!isSpace(code) && code !== 0x0A) { - break; - } - } - - // [link]( "title" ) - // ^^^^^^^ parsing link title - res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax); - if (pos < max && start !== pos && res.ok) { - title = res.str; - pos = res.pos; - - // [link]( "title" ) - // ^^ skipping these spaces - for (; pos < max; pos++) { - code = state.src.charCodeAt(pos); - if (!isSpace(code) && code !== 0x0A) { - break; - } - } - } - } - if (pos >= max || state.src.charCodeAt(pos) !== 0x29 /* ) */) { - // parsing a valid shortcut link failed, fallback to reference - parseReference = true; - } - pos++; - } - if (parseReference) { - // - // Link reference - // - if (typeof state.env.references === 'undefined') { - return false; - } - if (pos < max && state.src.charCodeAt(pos) === 0x5B /* [ */) { - start = pos + 1; - pos = state.md.helpers.parseLinkLabel(state, pos); - if (pos >= 0) { - label = state.src.slice(start, pos++); - } else { - pos = labelEnd + 1; - } - } else { - pos = labelEnd + 1; - } - - // covers label === '' and label === undefined - // (collapsed reference link and shortcut reference link respectively) - if (!label) { - label = state.src.slice(labelStart, labelEnd); - } - ref = state.env.references[normalizeReference(label)]; - if (!ref) { - state.pos = oldPos; - return false; - } - href = ref.href; - title = ref.title; - } - - // - // We found the end of the link, and know for a fact it's a valid link; - // so all that's left to do is to call tokenizer. - // - if (!silent) { - state.pos = labelStart; - state.posMax = labelEnd; - token = state.push('link_open', 'a', 1); - token.attrs = attrs = [['href', href]]; - if (title) { - attrs.push(['title', title]); - } - state.md.inline.tokenize(state); - token = state.push('link_close', 'a', -1); - } - state.pos = pos; - state.posMax = max; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/newline.js": -/*!*********************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/newline.js ***! - \*********************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Proceess '\n' - - - -var isSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isSpace); -module.exports = function newline(state, silent) { - var pmax, - max, - pos = state.pos; - if (state.src.charCodeAt(pos) !== 0x0A /* \n */) { - return false; - } - pmax = state.pending.length - 1; - max = state.posMax; - - // ' \n' -> hardbreak - // Lookup in pending chars is bad practice! Don't copy to other rules! - // Pending string is stored in concat mode, indexed lookups will cause - // convertion to flat mode. - if (!silent) { - if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) { - if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) { - state.pending = state.pending.replace(/ +$/, ''); - state.push('hardbreak', 'br', 0); - } else { - state.pending = state.pending.slice(0, -1); - state.push('softbreak', 'br', 0); - } - } else { - state.push('softbreak', 'br', 0); - } - } - pos++; - - // skip heading spaces for next line - while (pos < max && isSpace(state.src.charCodeAt(pos))) { - pos++; - } - state.pos = pos; - return true; -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/state_inline.js": -/*!**************************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/state_inline.js ***! - \**************************************************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - -// Inline parser state - - - -var Token = __webpack_require__(/*! ../token */ "../../../node_modules/markdown-it/lib/token.js"); -var isWhiteSpace = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isWhiteSpace); -var isPunctChar = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isPunctChar); -var isMdAsciiPunct = (__webpack_require__(/*! ../common/utils */ "../../../node_modules/markdown-it/lib/common/utils.js").isMdAsciiPunct); -function StateInline(src, md, env, outTokens) { - this.src = src; - this.env = env; - this.md = md; - this.tokens = outTokens; - this.tokens_meta = Array(outTokens.length); - this.pos = 0; - this.posMax = this.src.length; - this.level = 0; - this.pending = ''; - this.pendingLevel = 0; - - // Stores { start: end } pairs. Useful for backtrack - // optimization of pairs parse (emphasis, strikes). - this.cache = {}; - - // List of emphasis-like delimiters for current tag - this.delimiters = []; - - // Stack of delimiter lists for upper level tags - this._prev_delimiters = []; - - // backtick length => last seen position - this.backticks = {}; - this.backticksScanned = false; -} - -// Flush pending text -// -StateInline.prototype.pushPending = function () { - var token = new Token('text', '', 0); - token.content = this.pending; - token.level = this.pendingLevel; - this.tokens.push(token); - this.pending = ''; - return token; -}; - -// Push new token to "stream". -// If pending text exists - flush it as text token -// -StateInline.prototype.push = function (type, tag, nesting) { - if (this.pending) { - this.pushPending(); - } - var token = new Token(type, tag, nesting); - var token_meta = null; - if (nesting < 0) { - // closing tag - this.level--; - this.delimiters = this._prev_delimiters.pop(); - } - token.level = this.level; - if (nesting > 0) { - // opening tag - this.level++; - this._prev_delimiters.push(this.delimiters); - this.delimiters = []; - token_meta = { - delimiters: this.delimiters - }; - } - this.pendingLevel = this.level; - this.tokens.push(token); - this.tokens_meta.push(token_meta); - return token; -}; - -// Scan a sequence of emphasis-like markers, and determine whether -// it can start an emphasis sequence or end an emphasis sequence. -// -// - start - position to scan from (it should point at a valid marker); -// - canSplitWord - determine if these markers can be found inside a word -// -StateInline.prototype.scanDelims = function (start, canSplitWord) { - var pos = start, - lastChar, - nextChar, - count, - can_open, - can_close, - isLastWhiteSpace, - isLastPunctChar, - isNextWhiteSpace, - isNextPunctChar, - left_flanking = true, - right_flanking = true, - max = this.posMax, - marker = this.src.charCodeAt(start); - - // treat beginning of the line as a whitespace - lastChar = start > 0 ? this.src.charCodeAt(start - 1) : 0x20; - while (pos < max && this.src.charCodeAt(pos) === marker) { - pos++; - } - count = pos - start; - - // treat end of the line as a whitespace - nextChar = pos < max ? this.src.charCodeAt(pos) : 0x20; - isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar)); - isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar)); - isLastWhiteSpace = isWhiteSpace(lastChar); - isNextWhiteSpace = isWhiteSpace(nextChar); - if (isNextWhiteSpace) { - left_flanking = false; - } else if (isNextPunctChar) { - if (!(isLastWhiteSpace || isLastPunctChar)) { - left_flanking = false; - } - } - if (isLastWhiteSpace) { - right_flanking = false; - } else if (isLastPunctChar) { - if (!(isNextWhiteSpace || isNextPunctChar)) { - right_flanking = false; - } - } - if (!canSplitWord) { - can_open = left_flanking && (!right_flanking || isLastPunctChar); - can_close = right_flanking && (!left_flanking || isNextPunctChar); - } else { - can_open = left_flanking; - can_close = right_flanking; - } - return { - can_open: can_open, - can_close: can_close, - length: count - }; -}; - -// re-export Token class to use in block rules -StateInline.prototype.Token = Token; -module.exports = StateInline; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/strikethrough.js": -/*!***************************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/strikethrough.js ***! - \***************************************************************************/ -/***/ (function(module) { - -// ~~strike through~~ -// - - -// Insert each marker as a separate text token, and add it to delimiter list -// -module.exports.tokenize = function strikethrough(state, silent) { - var i, - scanned, - token, - len, - ch, - start = state.pos, - marker = state.src.charCodeAt(start); - if (silent) { - return false; - } - if (marker !== 0x7E /* ~ */) { - return false; - } - scanned = state.scanDelims(state.pos, true); - len = scanned.length; - ch = String.fromCharCode(marker); - if (len < 2) { - return false; - } - if (len % 2) { - token = state.push('text', '', 0); - token.content = ch; - len--; - } - for (i = 0; i < len; i += 2) { - token = state.push('text', '', 0); - token.content = ch + ch; - state.delimiters.push({ - marker: marker, - length: 0, - // disable "rule of 3" length checks meant for emphasis - jump: i / 2, - // for `~~` 1 marker = 2 characters - token: state.tokens.length - 1, - end: -1, - open: scanned.can_open, - close: scanned.can_close - }); - } - state.pos += scanned.length; - return true; -}; -function postProcess(state, delimiters) { - var i, - j, - startDelim, - endDelim, - token, - loneMarkers = [], - max = delimiters.length; - for (i = 0; i < max; i++) { - startDelim = delimiters[i]; - if (startDelim.marker !== 0x7E /* ~ */) { - continue; - } - if (startDelim.end === -1) { - continue; - } - endDelim = delimiters[startDelim.end]; - token = state.tokens[startDelim.token]; - token.type = 's_open'; - token.tag = 's'; - token.nesting = 1; - token.markup = '~~'; - token.content = ''; - token = state.tokens[endDelim.token]; - token.type = 's_close'; - token.tag = 's'; - token.nesting = -1; - token.markup = '~~'; - token.content = ''; - if (state.tokens[endDelim.token - 1].type === 'text' && state.tokens[endDelim.token - 1].content === '~') { - loneMarkers.push(endDelim.token - 1); - } - } - - // If a marker sequence has an odd number of characters, it's splitted - // like this: `~~~~~` -> `~` + `~~` + `~~`, leaving one marker at the - // start of the sequence. - // - // So, we have to move all those markers after subsequent s_close tags. - // - while (loneMarkers.length) { - i = loneMarkers.pop(); - j = i + 1; - while (j < state.tokens.length && state.tokens[j].type === 's_close') { - j++; - } - j--; - if (i !== j) { - token = state.tokens[j]; - state.tokens[j] = state.tokens[i]; - state.tokens[i] = token; - } - } -} - -// Walk through delimiter list and replace text tokens with tags -// -module.exports.postProcess = function strikethrough(state) { - var curr, - tokens_meta = state.tokens_meta, - max = state.tokens_meta.length; - postProcess(state, state.delimiters); - for (curr = 0; curr < max; curr++) { - if (tokens_meta[curr] && tokens_meta[curr].delimiters) { - postProcess(state, tokens_meta[curr].delimiters); - } - } -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/text.js": -/*!******************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/text.js ***! - \******************************************************************/ -/***/ (function(module) { - -// Skip text characters for text token, place those to pending buffer -// and increment current pos - - - -// Rule to skip pure text -// '{}$%@~+=:' reserved for extentions - -// !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~ - -// !!!! Don't confuse with "Markdown ASCII Punctuation" chars -// http://spec.commonmark.org/0.15/#ascii-punctuation-character -function isTerminatorChar(ch) { - switch (ch) { - case 0x0A /* \n */: - case 0x21 /* ! */: - case 0x23 /* # */: - case 0x24 /* $ */: - case 0x25 /* % */: - case 0x26 /* & */: - case 0x2A /* * */: - case 0x2B /* + */: - case 0x2D /* - */: - case 0x3A /* : */: - case 0x3C /* < */: - case 0x3D /* = */: - case 0x3E /* > */: - case 0x40 /* @ */: - case 0x5B /* [ */: - case 0x5C /* \ */: - case 0x5D /* ] */: - case 0x5E /* ^ */: - case 0x5F /* _ */: - case 0x60 /* ` */: - case 0x7B /* { */: - case 0x7D /* } */: - case 0x7E /* ~ */: - return true; - default: - return false; - } -} -module.exports = function text(state, silent) { - var pos = state.pos; - while (pos < state.posMax && !isTerminatorChar(state.src.charCodeAt(pos))) { - pos++; - } - if (pos === state.pos) { - return false; - } - if (!silent) { - state.pending += state.src.slice(state.pos, pos); - } - state.pos = pos; - return true; -}; - -// Alternative implementation, for memory. -// -// It costs 10% of performance, but allows extend terminators list, if place it -// to `ParcerInline` property. Probably, will switch to it sometime, such -// flexibility required. - -/* -var TERMINATOR_RE = /[\n!#$%&*+\-:<=>@[\\\]^_`{}~]/; - -module.exports = function text(state, silent) { - var pos = state.pos, - idx = state.src.slice(pos).search(TERMINATOR_RE); - - // first char is terminator -> empty text - if (idx === 0) { return false; } - - // no terminator -> text till end of string - if (idx < 0) { - if (!silent) { state.pending += state.src.slice(pos); } - state.pos = state.src.length; - return true; - } - - if (!silent) { state.pending += state.src.slice(pos, pos + idx); } - - state.pos += idx; - - return true; -};*/ - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/rules_inline/text_collapse.js": -/*!***************************************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/rules_inline/text_collapse.js ***! - \***************************************************************************/ -/***/ (function(module) { - -// Clean up tokens after emphasis and strikethrough postprocessing: -// merge adjacent text nodes into one and re-calculate all token levels -// -// This is necessary because initially emphasis delimiter markers (*, _, ~) -// are treated as their own separate text tokens. Then emphasis rule either -// leaves them as text (needed to merge with adjacent text) or turns them -// into opening/closing tags (which messes up levels inside). -// - - -module.exports = function text_collapse(state) { - var curr, - last, - level = 0, - tokens = state.tokens, - max = state.tokens.length; - for (curr = last = 0; curr < max; curr++) { - // re-calculate levels after emphasis/strikethrough turns some text nodes - // into opening/closing tags - if (tokens[curr].nesting < 0) level--; // closing tag - tokens[curr].level = level; - if (tokens[curr].nesting > 0) level++; // opening tag - - if (tokens[curr].type === 'text' && curr + 1 < max && tokens[curr + 1].type === 'text') { - // collapse two adjacent text nodes - tokens[curr + 1].content = tokens[curr].content + tokens[curr + 1].content; - } else { - if (curr !== last) { - tokens[last] = tokens[curr]; - } - last++; - } - } - if (curr !== last) { - tokens.length = last; - } -}; - -/***/ }), - -/***/ "../../../node_modules/markdown-it/lib/token.js": -/*!******************************************************!*\ - !*** ../../../node_modules/markdown-it/lib/token.js ***! - \******************************************************/ -/***/ (function(module) { - -// Token class - - - -/** - * class Token - **/ - -/** - * new Token(type, tag, nesting) - * - * Create new token and fill passed properties. - **/ -function Token(type, tag, nesting) { - /** - * Token#type -> String - * - * Type of the token (string, e.g. "paragraph_open") - **/ - this.type = type; - - /** - * Token#tag -> String - * - * html tag name, e.g. "p" - **/ - this.tag = tag; - - /** - * Token#attrs -> Array - * - * Html attributes. Format: `[ [ name1, value1 ], [ name2, value2 ] ]` - **/ - this.attrs = null; - - /** - * Token#map -> Array - * - * Source map info. Format: `[ line_begin, line_end ]` - **/ - this.map = null; - - /** - * Token#nesting -> Number - * - * Level change (number in {-1, 0, 1} set), where: - * - * - `1` means the tag is opening - * - `0` means the tag is self-closing - * - `-1` means the tag is closing - **/ - this.nesting = nesting; - - /** - * Token#level -> Number - * - * nesting level, the same as `state.level` - **/ - this.level = 0; - - /** - * Token#children -> Array - * - * An array of child nodes (inline and img tokens) - **/ - this.children = null; - - /** - * Token#content -> String - * - * In a case of self-closing tag (code, html, fence, etc.), - * it has contents of this tag. - **/ - this.content = ''; - - /** - * Token#markup -> String - * - * '*' or '_' for emphasis, fence string for fence, etc. - **/ - this.markup = ''; - - /** - * Token#info -> String - * - * Additional information: - * - * - Info string for "fence" tokens - * - The value "auto" for autolink "link_open" and "link_close" tokens - * - The string value of the item marker for ordered-list "list_item_open" tokens - **/ - this.info = ''; - - /** - * Token#meta -> Object - * - * A place for plugins to store an arbitrary data - **/ - this.meta = null; - - /** - * Token#block -> Boolean - * - * True for block-level tokens, false for inline tokens. - * Used in renderer to calculate line breaks - **/ - this.block = false; - - /** - * Token#hidden -> Boolean - * - * If it's true, ignore this element when rendering. Used for tight lists - * to hide paragraphs. - **/ - this.hidden = false; -} - -/** - * Token.attrIndex(name) -> Number - * - * Search attribute index by name. - **/ -Token.prototype.attrIndex = function attrIndex(name) { - var attrs, i, len; - if (!this.attrs) { - return -1; - } - attrs = this.attrs; - for (i = 0, len = attrs.length; i < len; i++) { - if (attrs[i][0] === name) { - return i; - } - } - return -1; -}; - -/** - * Token.attrPush(attrData) - * - * Add `[ name, value ]` attribute to list. Init attrs if necessary - **/ -Token.prototype.attrPush = function attrPush(attrData) { - if (this.attrs) { - this.attrs.push(attrData); - } else { - this.attrs = [attrData]; - } -}; - -/** - * Token.attrSet(name, value) - * - * Set `name` attribute to `value`. Override old value if exists. - **/ -Token.prototype.attrSet = function attrSet(name, value) { - var idx = this.attrIndex(name), - attrData = [name, value]; - if (idx < 0) { - this.attrPush(attrData); - } else { - this.attrs[idx] = attrData; - } -}; - -/** - * Token.attrGet(name) - * - * Get the value of attribute `name`, or null if it does not exist. - **/ -Token.prototype.attrGet = function attrGet(name) { - var idx = this.attrIndex(name), - value = null; - if (idx >= 0) { - value = this.attrs[idx][1]; - } - return value; -}; - -/** - * Token.attrJoin(name, value) - * - * Join value to existing attribute via space. Or create new attribute if not - * exists. Useful to operate with token classes. - **/ -Token.prototype.attrJoin = function attrJoin(name, value) { - var idx = this.attrIndex(name); - if (idx < 0) { - this.attrPush([name, value]); - } else { - this.attrs[idx][1] = this.attrs[idx][1] + ' ' + value; - } -}; -module.exports = Token; - -/***/ }), - -/***/ "../../../node_modules/mdurl/decode.js": -/*!*********************************************!*\ - !*** ../../../node_modules/mdurl/decode.js ***! - \*********************************************/ -/***/ (function(module) { - - - -/* eslint-disable no-bitwise */ -var decodeCache = {}; -function getDecodeCache(exclude) { - var i, - ch, - cache = decodeCache[exclude]; - if (cache) { - return cache; - } - cache = decodeCache[exclude] = []; - for (i = 0; i < 128; i++) { - ch = String.fromCharCode(i); - cache.push(ch); - } - for (i = 0; i < exclude.length; i++) { - ch = exclude.charCodeAt(i); - cache[ch] = '%' + ('0' + ch.toString(16).toUpperCase()).slice(-2); - } - return cache; -} - -// Decode percent-encoded string. -// -function decode(string, exclude) { - var cache; - if (typeof exclude !== 'string') { - exclude = decode.defaultChars; - } - cache = getDecodeCache(exclude); - return string.replace(/(%[a-f0-9]{2})+/gi, function (seq) { - var i, - l, - b1, - b2, - b3, - b4, - chr, - result = ''; - for (i = 0, l = seq.length; i < l; i += 3) { - b1 = parseInt(seq.slice(i + 1, i + 3), 16); - if (b1 < 0x80) { - result += cache[b1]; - continue; - } - if ((b1 & 0xE0) === 0xC0 && i + 3 < l) { - // 110xxxxx 10xxxxxx - b2 = parseInt(seq.slice(i + 4, i + 6), 16); - if ((b2 & 0xC0) === 0x80) { - chr = b1 << 6 & 0x7C0 | b2 & 0x3F; - if (chr < 0x80) { - result += '\ufffd\ufffd'; - } else { - result += String.fromCharCode(chr); - } - i += 3; - continue; - } - } - if ((b1 & 0xF0) === 0xE0 && i + 6 < l) { - // 1110xxxx 10xxxxxx 10xxxxxx - b2 = parseInt(seq.slice(i + 4, i + 6), 16); - b3 = parseInt(seq.slice(i + 7, i + 9), 16); - if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) { - chr = b1 << 12 & 0xF000 | b2 << 6 & 0xFC0 | b3 & 0x3F; - if (chr < 0x800 || chr >= 0xD800 && chr <= 0xDFFF) { - result += '\ufffd\ufffd\ufffd'; - } else { - result += String.fromCharCode(chr); - } - i += 6; - continue; - } - } - if ((b1 & 0xF8) === 0xF0 && i + 9 < l) { - // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx - b2 = parseInt(seq.slice(i + 4, i + 6), 16); - b3 = parseInt(seq.slice(i + 7, i + 9), 16); - b4 = parseInt(seq.slice(i + 10, i + 12), 16); - if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80 && (b4 & 0xC0) === 0x80) { - chr = b1 << 18 & 0x1C0000 | b2 << 12 & 0x3F000 | b3 << 6 & 0xFC0 | b4 & 0x3F; - if (chr < 0x10000 || chr > 0x10FFFF) { - result += '\ufffd\ufffd\ufffd\ufffd'; - } else { - chr -= 0x10000; - result += String.fromCharCode(0xD800 + (chr >> 10), 0xDC00 + (chr & 0x3FF)); - } - i += 9; - continue; - } - } - result += '\ufffd'; - } - return result; - }); -} -decode.defaultChars = ';/?:@&=+$,#'; -decode.componentChars = ''; -module.exports = decode; - -/***/ }), - -/***/ "../../../node_modules/mdurl/encode.js": -/*!*********************************************!*\ - !*** ../../../node_modules/mdurl/encode.js ***! - \*********************************************/ -/***/ (function(module) { - - - -var encodeCache = {}; - -// Create a lookup array where anything but characters in `chars` string -// and alphanumeric chars is percent-encoded. -// -function getEncodeCache(exclude) { - var i, - ch, - cache = encodeCache[exclude]; - if (cache) { - return cache; - } - cache = encodeCache[exclude] = []; - for (i = 0; i < 128; i++) { - ch = String.fromCharCode(i); - if (/^[0-9a-z]$/i.test(ch)) { - // always allow unencoded alphanumeric characters - cache.push(ch); - } else { - cache.push('%' + ('0' + i.toString(16).toUpperCase()).slice(-2)); - } - } - for (i = 0; i < exclude.length; i++) { - cache[exclude.charCodeAt(i)] = exclude[i]; - } - return cache; -} - -// Encode unsafe characters with percent-encoding, skipping already -// encoded sequences. -// -// - string - string to encode -// - exclude - list of characters to ignore (in addition to a-zA-Z0-9) -// - keepEscaped - don't encode '%' in a correct escape sequence (default: true) -// -function encode(string, exclude, keepEscaped) { - var i, - l, - code, - nextCode, - cache, - result = ''; - if (typeof exclude !== 'string') { - // encode(string, keepEscaped) - keepEscaped = exclude; - exclude = encode.defaultChars; - } - if (typeof keepEscaped === 'undefined') { - keepEscaped = true; - } - cache = getEncodeCache(exclude); - for (i = 0, l = string.length; i < l; i++) { - code = string.charCodeAt(i); - if (keepEscaped && code === 0x25 /* % */ && i + 2 < l) { - if (/^[0-9a-f]{2}$/i.test(string.slice(i + 1, i + 3))) { - result += string.slice(i, i + 3); - i += 2; - continue; - } - } - if (code < 128) { - result += cache[code]; - continue; - } - if (code >= 0xD800 && code <= 0xDFFF) { - if (code >= 0xD800 && code <= 0xDBFF && i + 1 < l) { - nextCode = string.charCodeAt(i + 1); - if (nextCode >= 0xDC00 && nextCode <= 0xDFFF) { - result += encodeURIComponent(string[i] + string[i + 1]); - i++; - continue; - } - } - result += '%EF%BF%BD'; - continue; - } - result += encodeURIComponent(string[i]); - } - return result; -} -encode.defaultChars = ";/?:@&=+$,-_.!~*'()#"; -encode.componentChars = "-_.!~*'()"; -module.exports = encode; - -/***/ }), - -/***/ "../../../node_modules/mdurl/format.js": -/*!*********************************************!*\ - !*** ../../../node_modules/mdurl/format.js ***! - \*********************************************/ -/***/ (function(module) { - - - -module.exports = function format(url) { - var result = ''; - result += url.protocol || ''; - result += url.slashes ? '//' : ''; - result += url.auth ? url.auth + '@' : ''; - if (url.hostname && url.hostname.indexOf(':') !== -1) { - // ipv6 address - result += '[' + url.hostname + ']'; - } else { - result += url.hostname || ''; - } - result += url.port ? ':' + url.port : ''; - result += url.pathname || ''; - result += url.search || ''; - result += url.hash || ''; - return result; -}; - -/***/ }), - -/***/ "../../../node_modules/mdurl/index.js": -/*!********************************************!*\ - !*** ../../../node_modules/mdurl/index.js ***! - \********************************************/ -/***/ (function(module, __unused_webpack_exports, __webpack_require__) { - - - -module.exports.encode = __webpack_require__(/*! ./encode */ "../../../node_modules/mdurl/encode.js"); -module.exports.decode = __webpack_require__(/*! ./decode */ "../../../node_modules/mdurl/decode.js"); -module.exports.format = __webpack_require__(/*! ./format */ "../../../node_modules/mdurl/format.js"); -module.exports.parse = __webpack_require__(/*! ./parse */ "../../../node_modules/mdurl/parse.js"); - -/***/ }), - -/***/ "../../../node_modules/mdurl/parse.js": -/*!********************************************!*\ - !*** ../../../node_modules/mdurl/parse.js ***! - \********************************************/ -/***/ (function(module) { - -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -// -// Changes from joyent/node: -// -// 1. No leading slash in paths, -// e.g. in `url.parse('http://foo?bar')` pathname is ``, not `/` -// -// 2. Backslashes are not replaced with slashes, -// so `http:\\example.org\` is treated like a relative path -// -// 3. Trailing colon is treated like a part of the path, -// i.e. in `http://example.org:foo` pathname is `:foo` -// -// 4. Nothing is URL-encoded in the resulting object, -// (in joyent/node some chars in auth and paths are encoded) -// -// 5. `url.parse()` does not have `parseQueryString` argument -// -// 6. Removed extraneous result properties: `host`, `path`, `query`, etc., -// which can be constructed using other parts of the url. -// -function Url() { - this.protocol = null; - this.slashes = null; - this.auth = null; - this.port = null; - this.hostname = null; - this.hash = null; - this.search = null; - this.pathname = null; -} - -// Reference: RFC 3986, RFC 1808, RFC 2396 - -// define these here so at least they only have to be -// compiled once on the first module load. -var protocolPattern = /^([a-z0-9.+-]+:)/i, - portPattern = /:[0-9]*$/, - // Special case for a simple path URL - simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/, - // RFC 2396: characters reserved for delimiting URLs. - // We actually just auto-escape these. - delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'], - // RFC 2396: characters not allowed for various reasons. - unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims), - // Allowed by RFCs, but cause of XSS attacks. Always escape these. - autoEscape = ['\''].concat(unwise), - // Characters that are never ever allowed in a hostname. - // Note that any invalid chars are also handled, but these - // are the ones that are *expected* to be seen, so we fast-path - // them. - nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape), - hostEndingChars = ['/', '?', '#'], - hostnameMaxLen = 255, - hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/, - hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/, - // protocols that can allow "unsafe" and "unwise" chars. - /* eslint-disable no-script-url */ - // protocols that never have a hostname. - hostlessProtocol = { - 'javascript': true, - 'javascript:': true - }, - // protocols that always contain a // bit. - slashedProtocol = { - 'http': true, - 'https': true, - 'ftp': true, - 'gopher': true, - 'file': true, - 'http:': true, - 'https:': true, - 'ftp:': true, - 'gopher:': true, - 'file:': true - }; -/* eslint-enable no-script-url */ - -function urlParse(url, slashesDenoteHost) { - if (url && url instanceof Url) { - return url; - } - var u = new Url(); - u.parse(url, slashesDenoteHost); - return u; -} -Url.prototype.parse = function (url, slashesDenoteHost) { - var i, - l, - lowerProto, - hec, - slashes, - rest = url; - - // trim before proceeding. - // This is to support parse stuff like " http://foo.com \n" - rest = rest.trim(); - if (!slashesDenoteHost && url.split('#').length === 1) { - // Try fast path regexp - var simplePath = simplePathPattern.exec(rest); - if (simplePath) { - this.pathname = simplePath[1]; - if (simplePath[2]) { - this.search = simplePath[2]; - } - return this; - } - } - var proto = protocolPattern.exec(rest); - if (proto) { - proto = proto[0]; - lowerProto = proto.toLowerCase(); - this.protocol = proto; - rest = rest.substr(proto.length); - } - - // figure out if it's got a host - // user@server is *always* interpreted as a hostname, and url - // resolution will treat //foo/bar as host=foo,path=bar because that's - // how the browser resolves relative URLs. - if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { - slashes = rest.substr(0, 2) === '//'; - if (slashes && !(proto && hostlessProtocol[proto])) { - rest = rest.substr(2); - this.slashes = true; - } - } - if (!hostlessProtocol[proto] && (slashes || proto && !slashedProtocol[proto])) { - // there's a hostname. - // the first instance of /, ?, ;, or # ends the host. - // - // If there is an @ in the hostname, then non-host chars *are* allowed - // to the left of the last @ sign, unless some host-ending character - // comes *before* the @-sign. - // URLs are obnoxious. - // - // ex: - // http://a@b@c/ => user:a@b host:c - // http://a@b?@c => user:a host:c path:/?@c - - // v0.12 TODO(isaacs): This is not quite how Chrome does things. - // Review our test case against browsers more comprehensively. - - // find the first instance of any hostEndingChars - var hostEnd = -1; - for (i = 0; i < hostEndingChars.length; i++) { - hec = rest.indexOf(hostEndingChars[i]); - if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) { - hostEnd = hec; - } - } - - // at this point, either we have an explicit point where the - // auth portion cannot go past, or the last @ char is the decider. - var auth, atSign; - if (hostEnd === -1) { - // atSign can be anywhere. - atSign = rest.lastIndexOf('@'); - } else { - // atSign must be in auth portion. - // http://a@b/c@d => host:b auth:a path:/c@d - atSign = rest.lastIndexOf('@', hostEnd); - } - - // Now we have a portion which is definitely the auth. - // Pull that off. - if (atSign !== -1) { - auth = rest.slice(0, atSign); - rest = rest.slice(atSign + 1); - this.auth = auth; - } - - // the host is the remaining to the left of the first non-host char - hostEnd = -1; - for (i = 0; i < nonHostChars.length; i++) { - hec = rest.indexOf(nonHostChars[i]); - if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) { - hostEnd = hec; - } - } - // if we still have not hit it, then the entire thing is a host. - if (hostEnd === -1) { - hostEnd = rest.length; - } - if (rest[hostEnd - 1] === ':') { - hostEnd--; - } - var host = rest.slice(0, hostEnd); - rest = rest.slice(hostEnd); - - // pull out port. - this.parseHost(host); - - // we've indicated that there is a hostname, - // so even if it's empty, it has to be present. - this.hostname = this.hostname || ''; - - // if hostname begins with [ and ends with ] - // assume that it's an IPv6 address. - var ipv6Hostname = this.hostname[0] === '[' && this.hostname[this.hostname.length - 1] === ']'; - - // validate a little. - if (!ipv6Hostname) { - var hostparts = this.hostname.split(/\./); - for (i = 0, l = hostparts.length; i < l; i++) { - var part = hostparts[i]; - if (!part) { - continue; - } - if (!part.match(hostnamePartPattern)) { - var newpart = ''; - for (var j = 0, k = part.length; j < k; j++) { - if (part.charCodeAt(j) > 127) { - // we replace non-ASCII char with a temporary placeholder - // we need this to make sure size of hostname is not - // broken by replacing non-ASCII by nothing - newpart += 'x'; - } else { - newpart += part[j]; - } - } - // we test again with ASCII char only - if (!newpart.match(hostnamePartPattern)) { - var validParts = hostparts.slice(0, i); - var notHost = hostparts.slice(i + 1); - var bit = part.match(hostnamePartStart); - if (bit) { - validParts.push(bit[1]); - notHost.unshift(bit[2]); - } - if (notHost.length) { - rest = notHost.join('.') + rest; - } - this.hostname = validParts.join('.'); - break; - } - } - } - } - if (this.hostname.length > hostnameMaxLen) { - this.hostname = ''; - } - - // strip [ and ] from the hostname - // the host field still retains them, though - if (ipv6Hostname) { - this.hostname = this.hostname.substr(1, this.hostname.length - 2); - } - } - - // chop off from the tail first. - var hash = rest.indexOf('#'); - if (hash !== -1) { - // got a fragment string. - this.hash = rest.substr(hash); - rest = rest.slice(0, hash); - } - var qm = rest.indexOf('?'); - if (qm !== -1) { - this.search = rest.substr(qm); - rest = rest.slice(0, qm); - } - if (rest) { - this.pathname = rest; - } - if (slashedProtocol[lowerProto] && this.hostname && !this.pathname) { - this.pathname = ''; - } - return this; -}; -Url.prototype.parseHost = function (host) { - var port = portPattern.exec(host); - if (port) { - port = port[0]; - if (port !== ':') { - this.port = port.substr(1); - } - host = host.substr(0, host.length - port.length); - } - if (host) { - this.hostname = host; - } -}; -module.exports = urlParse; - -/***/ }), - /***/ "../../../node_modules/meros/browser/index.mjs": /*!*****************************************************!*\ !*** ../../../node_modules/meros/browser/index.mjs ***! @@ -48118,10 +42122,10 @@ exports.wrap = wrap; /***/ }), -/***/ "../../../node_modules/punycode/punycode.es6.js": -/*!******************************************************!*\ - !*** ../../../node_modules/punycode/punycode.es6.js ***! - \******************************************************/ +/***/ "../../../node_modules/punycode.js/punycode.es6.js": +/*!*********************************************************!*\ + !*** ../../../node_modules/punycode.js/punycode.es6.js ***! + \*********************************************************/ /***/ (function(__unused_webpack_module, exports) { @@ -48147,7 +42151,7 @@ const delimiter = '-'; // '\x2D' /** Regular expressions */ const regexPunycode = /^xn--/; -const regexNonASCII = /[^\0-\x7E]/; // non-ASCII chars +const regexNonASCII = /[^\0-\x7F]/; // Note: U+007F DEL is excluded too. const regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators /** Error messages */ @@ -48182,11 +42186,11 @@ function error(type) { * item. * @returns {Array} A new array of values returned by the callback function. */ -function map(array, fn) { +function map(array, callback) { const result = []; let length = array.length; while (length--) { - result[length] = fn(array[length]); + result[length] = callback(array[length]); } return result; } @@ -48198,22 +42202,22 @@ function map(array, fn) { * @param {String} domain The domain name or email address. * @param {Function} callback The function that gets called for every * character. - * @returns {Array} A new string of characters returned by the callback + * @returns {String} A new string of characters returned by the callback * function. */ -function mapDomain(string, fn) { - const parts = string.split('@'); +function mapDomain(domain, callback) { + const parts = domain.split('@'); let result = ''; if (parts.length > 1) { // In email addresses, only the domain name should be punycoded. Leave // the local part (i.e. everything up to `@`) intact. result = parts[0] + '@'; - string = parts[1]; + domain = parts[1]; } // Avoid `split(regex)` for IE8 compatibility. See #17. - string = string.replace(regexSeparators, '\x2E'); - const labels = string.split('.'); - const encoded = map(labels, fn).join('.'); + domain = domain.replace(regexSeparators, '\x2E'); + const labels = domain.split('.'); + const encoded = map(labels, callback).join('.'); return result + encoded; } @@ -48263,7 +42267,7 @@ function ucs2decode(string) { * @param {Array} codePoints The array of numeric code points. * @returns {String} The new Unicode string (UCS-2). */ -const ucs2encode = array => String.fromCodePoint(...array); +const ucs2encode = codePoints => String.fromCodePoint(...codePoints); /** * Converts a basic code point into a digit/integer. @@ -48276,13 +42280,13 @@ const ucs2encode = array => String.fromCodePoint(...array); */ exports.ucs2encode = ucs2encode; const basicToDigit = function (codePoint) { - if (codePoint - 0x30 < 0x0A) { - return codePoint - 0x16; + if (codePoint >= 0x30 && codePoint < 0x3A) { + return 26 + (codePoint - 0x30); } - if (codePoint - 0x41 < 0x1A) { + if (codePoint >= 0x41 && codePoint < 0x5B) { return codePoint - 0x41; } - if (codePoint - 0x61 < 0x1A) { + if (codePoint >= 0x61 && codePoint < 0x7B) { return codePoint - 0x61; } return base; @@ -48362,14 +42366,17 @@ const decode = function (input) { // which gets added to `i`. The overflow checking is easier // if we increase `i` as we go, then subtract off its starting // value at the end to obtain `delta`. - let oldi = i; + const oldi = i; for /* no condition */ (let w = 1, k = base;; k += base) { if (index >= inputLength) { error('invalid-input'); } const digit = basicToDigit(input.charCodeAt(index++)); - if (digit >= base || digit > floor((maxInt - i) / w)) { + if (digit >= base) { + error('invalid-input'); + } + if (digit > floor((maxInt - i) / w)) { error('overflow'); } i += digit * w; @@ -48415,7 +42422,7 @@ const encode = function (input) { input = ucs2decode(input); // Cache the length. - let inputLength = input.length; + const inputLength = input.length; // Initialize the state. let n = initialN; @@ -48428,7 +42435,7 @@ const encode = function (input) { output.push(stringFromCharCode(currentValue)); } } - let basicLength = output.length; + const basicLength = output.length; let handledCPCount = basicLength; // `handledCPCount` is the number of code points that have been handled; @@ -48462,7 +42469,7 @@ const encode = function (input) { if (currentValue < n && ++delta > maxInt) { error('overflow'); } - if (currentValue == n) { + if (currentValue === n) { // Represent delta as a generalized variable-length integer. let q = delta; for /* no condition */ @@ -48477,7 +42484,7 @@ const encode = function (input) { q = floor(qMinusT / baseMinusT); } output.push(stringFromCharCode(digitToBasic(q, 0))); - bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); + bias = adapt(delta, handledCPCountPlusOne, handledCPCount === basicLength); delta = 0; ++handledCPCount; } @@ -48534,7 +42541,7 @@ const punycode = { * @memberOf punycode * @type String */ - 'version': '2.1.0', + 'version': '2.3.1', /** * An object of methods to convert from JavaScript's internal character * representation (UCS-2) to Unicode code points, and back. @@ -51424,82 +45431,6 @@ function __classPrivateFieldIn(state, receiver) { /***/ }), -/***/ "../../../node_modules/uc.micro/categories/Cc/regex.js": -/*!*************************************************************!*\ - !*** ../../../node_modules/uc.micro/categories/Cc/regex.js ***! - \*************************************************************/ -/***/ (function(module) { - - - -module.exports = /[\0-\x1F\x7F-\x9F]/; - -/***/ }), - -/***/ "../../../node_modules/uc.micro/categories/Cf/regex.js": -/*!*************************************************************!*\ - !*** ../../../node_modules/uc.micro/categories/Cf/regex.js ***! - \*************************************************************/ -/***/ (function(module) { - - - -module.exports = /[\xAD\u0600-\u0605\u061C\u06DD\u070F\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804[\uDCBD\uDCCD]|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/; - -/***/ }), - -/***/ "../../../node_modules/uc.micro/categories/P/regex.js": -/*!************************************************************!*\ - !*** ../../../node_modules/uc.micro/categories/P/regex.js ***! - \************************************************************/ -/***/ (function(module) { - - - -module.exports = /[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4E\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDF55-\uDF59]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD806[\uDC3B\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDE97-\uDE9A]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/; - -/***/ }), - -/***/ "../../../node_modules/uc.micro/categories/Z/regex.js": -/*!************************************************************!*\ - !*** ../../../node_modules/uc.micro/categories/Z/regex.js ***! - \************************************************************/ -/***/ (function(module) { - - - -module.exports = /[ \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/; - -/***/ }), - -/***/ "../../../node_modules/uc.micro/index.js": -/*!***********************************************!*\ - !*** ../../../node_modules/uc.micro/index.js ***! - \***********************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -exports.Any = __webpack_require__(/*! ./properties/Any/regex */ "../../../node_modules/uc.micro/properties/Any/regex.js"); -exports.Cc = __webpack_require__(/*! ./categories/Cc/regex */ "../../../node_modules/uc.micro/categories/Cc/regex.js"); -exports.Cf = __webpack_require__(/*! ./categories/Cf/regex */ "../../../node_modules/uc.micro/categories/Cf/regex.js"); -exports.P = __webpack_require__(/*! ./categories/P/regex */ "../../../node_modules/uc.micro/categories/P/regex.js"); -exports.Z = __webpack_require__(/*! ./categories/Z/regex */ "../../../node_modules/uc.micro/categories/Z/regex.js"); - -/***/ }), - -/***/ "../../../node_modules/uc.micro/properties/Any/regex.js": -/*!**************************************************************!*\ - !*** ../../../node_modules/uc.micro/properties/Any/regex.js ***! - \**************************************************************/ -/***/ (function(module) { - - - -module.exports = /[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; - -/***/ }), - /***/ "../../../node_modules/use-callback-ref/dist/es2015/assignRef.js": /*!***********************************************************************!*\ !*** ../../../node_modules/use-callback-ref/dist/es2015/assignRef.js ***! @@ -62028,141 +55959,119 @@ exports.comment = Q; -var l = Object.defineProperty; -var n = (e, o) => l(e, "name", { - value: o, +var E = Object.defineProperty; +var g = (v, m) => E(v, "name", { + value: m, configurable: !0 }); -const s = __webpack_require__(/*! ./codemirror.cjs2.js */ "../../graphiql-react/dist/codemirror.cjs2.js"), - g = __webpack_require__(/*! ./dialog.cjs2.js */ "../../graphiql-react/dist/dialog.cjs2.js"); -function u(e, o) { - for (var i = 0; i < o.length; i++) { - const r = o[i]; +const b = __webpack_require__(/*! ./codemirror.cjs2.js */ "../../graphiql-react/dist/codemirror.cjs2.js"); +function O(v, m) { + for (var n = 0; n < m.length; n++) { + const r = m[n]; if (typeof r != "string" && !Array.isArray(r)) { - for (const t in r) if (t !== "default" && !(t in e)) { - const a = Object.getOwnPropertyDescriptor(r, t); - a && Object.defineProperty(e, t, a.get ? a : { + for (const c in r) if (c !== "default" && !(c in v)) { + const u = Object.getOwnPropertyDescriptor(r, c); + u && Object.defineProperty(v, c, u.get ? u : { enumerable: !0, - get: () => r[t] + get: () => r[c] }); } } } - return Object.freeze(Object.defineProperty(e, Symbol.toStringTag, { + return Object.freeze(Object.defineProperty(v, Symbol.toStringTag, { value: "Module" })); } -n(u, "_mergeNamespaces"); -var c = g.requireDialog(); -const f = s.getDefaultExportFromCjs(c), - d = u({ - __proto__: null, - default: f - }, [c]); -exports.dialog = d; - -/***/ }), - -/***/ "../../graphiql-react/dist/dialog.cjs2.js": -/*!************************************************!*\ - !*** ../../graphiql-react/dist/dialog.cjs2.js ***! - \************************************************/ -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - - - -var D = Object.defineProperty; -var r = (m, p) => D(m, "name", { - value: p, - configurable: !0 -}); -const E = __webpack_require__(/*! ./codemirror.cjs2.js */ "../../graphiql-react/dist/codemirror.cjs2.js"); -var h = { - exports: {} - }, - y; -function T() { - return y || (y = 1, function (m, p) { - (function (n) { - n(E.requireCodemirror()); - })(function (n) { - function d(f, o, e) { - var i = f.getWrapperElement(), - a; - return a = i.appendChild(document.createElement("div")), e ? a.className = "CodeMirror-dialog CodeMirror-dialog-bottom" : a.className = "CodeMirror-dialog CodeMirror-dialog-top", typeof o == "string" ? a.innerHTML = o : a.appendChild(o), n.addClass(i, "dialog-opened"), a; - } - r(d, "dialogDiv"); - function g(f, o) { - f.state.currentNotificationClose && f.state.currentNotificationClose(), f.state.currentNotificationClose = o; - } - r(g, "closeNotification"), n.defineExtension("openDialog", function (f, o, e) { - e || (e = {}), g(this, null); - var i = d(this, f, e.bottom), - a = !1, - c = this; - function l(t) { - if (typeof t == "string") u.value = t;else { - if (a) return; - a = !0, n.rmClass(i.parentNode, "dialog-opened"), i.parentNode.removeChild(i), c.focus(), e.onClose && e.onClose(i); - } +g(O, "_mergeNamespaces"); +var T = { + exports: {} +}; +(function (v, m) { + (function (n) { + n(b.requireCodemirror()); + })(function (n) { + function r(u, f, e) { + var a = u.getWrapperElement(), + l; + return l = a.appendChild(document.createElement("div")), e ? l.className = "CodeMirror-dialog CodeMirror-dialog-bottom" : l.className = "CodeMirror-dialog CodeMirror-dialog-top", typeof f == "string" ? l.innerHTML = f : l.appendChild(f), n.addClass(a, "dialog-opened"), l; + } + g(r, "dialogDiv"); + function c(u, f) { + u.state.currentNotificationClose && u.state.currentNotificationClose(), u.state.currentNotificationClose = f; + } + g(c, "closeNotification"), n.defineExtension("openDialog", function (u, f, e) { + e || (e = {}), c(this, null); + var a = r(this, u, e.bottom), + l = !1, + s = this; + function i(t) { + if (typeof t == "string") o.value = t;else { + if (l) return; + l = !0, n.rmClass(a.parentNode, "dialog-opened"), a.parentNode.removeChild(a), s.focus(), e.onClose && e.onClose(a); } - r(l, "close"); - var u = i.getElementsByTagName("input")[0], - s; - return u ? (u.focus(), e.value && (u.value = e.value, e.selectValueOnOpen !== !1 && u.select()), e.onInput && n.on(u, "input", function (t) { - e.onInput(t, u.value, l); - }), e.onKeyUp && n.on(u, "keyup", function (t) { - e.onKeyUp(t, u.value, l); - }), n.on(u, "keydown", function (t) { - e && e.onKeyDown && e.onKeyDown(t, u.value, l) || ((t.keyCode == 27 || e.closeOnEnter !== !1 && t.keyCode == 13) && (u.blur(), n.e_stop(t), l()), t.keyCode == 13 && o(u.value, t)); - }), e.closeOnBlur !== !1 && n.on(i, "focusout", function (t) { - t.relatedTarget !== null && l(); - })) : (s = i.getElementsByTagName("button")[0]) && (n.on(s, "click", function () { - l(), c.focus(); - }), e.closeOnBlur !== !1 && n.on(s, "blur", l), s.focus()), l; - }), n.defineExtension("openConfirm", function (f, o, e) { - g(this, null); - var i = d(this, f, e && e.bottom), - a = i.getElementsByTagName("button"), - c = !1, - l = this, - u = 1; - function s() { - c || (c = !0, n.rmClass(i.parentNode, "dialog-opened"), i.parentNode.removeChild(i), l.focus()); - } - r(s, "close"), a[0].focus(); - for (var t = 0; t < a.length; ++t) { - var v = a[t]; - (function (N) { - n.on(v, "click", function (b) { - n.e_preventDefault(b), s(), N && N(l); - }); - })(o[t]), n.on(v, "blur", function () { - --u, setTimeout(function () { - u <= 0 && s(); - }, 200); - }), n.on(v, "focus", function () { - ++u; + } + g(i, "close"); + var o = a.getElementsByTagName("input")[0], + d; + return o ? (o.focus(), e.value && (o.value = e.value, e.selectValueOnOpen !== !1 && o.select()), e.onInput && n.on(o, "input", function (t) { + e.onInput(t, o.value, i); + }), e.onKeyUp && n.on(o, "keyup", function (t) { + e.onKeyUp(t, o.value, i); + }), n.on(o, "keydown", function (t) { + e && e.onKeyDown && e.onKeyDown(t, o.value, i) || ((t.keyCode == 27 || e.closeOnEnter !== !1 && t.keyCode == 13) && (o.blur(), n.e_stop(t), i()), t.keyCode == 13 && f(o.value, t)); + }), e.closeOnBlur !== !1 && n.on(a, "focusout", function (t) { + t.relatedTarget !== null && i(); + })) : (d = a.getElementsByTagName("button")[0]) && (n.on(d, "click", function () { + i(), s.focus(); + }), e.closeOnBlur !== !1 && n.on(d, "blur", i), d.focus()), i; + }), n.defineExtension("openConfirm", function (u, f, e) { + c(this, null); + var a = r(this, u, e && e.bottom), + l = a.getElementsByTagName("button"), + s = !1, + i = this, + o = 1; + function d() { + s || (s = !0, n.rmClass(a.parentNode, "dialog-opened"), a.parentNode.removeChild(a), i.focus()); + } + g(d, "close"), l[0].focus(); + for (var t = 0; t < l.length; ++t) { + var p = l[t]; + (function (N) { + n.on(p, "click", function (h) { + n.e_preventDefault(h), d(), N && N(i); }); - } - }), n.defineExtension("openNotification", function (f, o) { - g(this, l); - var e = d(this, f, o && o.bottom), - i = !1, - a, - c = o && typeof o.duration < "u" ? o.duration : 5e3; - function l() { - i || (i = !0, clearTimeout(a), n.rmClass(e.parentNode, "dialog-opened"), e.parentNode.removeChild(e)); - } - return r(l, "close"), n.on(e, "click", function (u) { - n.e_preventDefault(u), l(); - }), c && (a = setTimeout(l, c)), l; - }); + })(f[t]), n.on(p, "blur", function () { + --o, setTimeout(function () { + o <= 0 && d(); + }, 200); + }), n.on(p, "focus", function () { + ++o; + }); + } + }), n.defineExtension("openNotification", function (u, f) { + c(this, i); + var e = r(this, u, f && f.bottom), + a = !1, + l, + s = f && typeof f.duration < "u" ? f.duration : 5e3; + function i() { + a || (a = !0, clearTimeout(l), n.rmClass(e.parentNode, "dialog-opened"), e.parentNode.removeChild(e)); + } + return g(i, "close"), n.on(e, "click", function (o) { + n.e_preventDefault(o), i(); + }), s && (l = setTimeout(i, s)), i; }); - }()), h.exports; -} -r(T, "requireDialog"); -exports.requireDialog = T; + }); +})(); +var y = T.exports; +const x = b.getDefaultExportFromCjs(y), + k = O({ + __proto__: null, + default: x + }, [y]); +exports.dialog = k; +exports.dialogExports = y; /***/ }), @@ -62704,7 +56613,7 @@ const r = __webpack_require__(/*! react/jsx-runtime */ "../../../node_modules/re Pn = __webpack_require__(/*! @radix-ui/react-dialog */ "../../../node_modules/@radix-ui/react-dialog/dist/index.js"), qn = __webpack_require__(/*! @radix-ui/react-visually-hidden */ "../../../node_modules/@radix-ui/react-visually-hidden/dist/index.js"), xe = __webpack_require__(/*! @radix-ui/react-dropdown-menu */ "../../../node_modules/@radix-ui/react-dropdown-menu/dist/index.js"), - Vn = __webpack_require__(/*! markdown-it */ "../../../node_modules/markdown-it/index.js"), + Vn = __webpack_require__(/*! markdown-it */ "../node_modules/markdown-it/dist/index.cjs.js"), Nt = __webpack_require__(/*! framer-motion */ "../../../node_modules/framer-motion/dist/cjs/index.js"), In = __webpack_require__(/*! @radix-ui/react-tooltip */ "../../../node_modules/@radix-ui/react-tooltip/dist/index.js"), ae = __webpack_require__(/*! @headlessui/react */ "../../../node_modules/@headlessui/react/dist/index.cjs"), @@ -62727,7 +56636,7 @@ function nt(e) { return t.default = e, Object.freeze(t); } a(nt, "_interopNamespaceDefault"); -const c = nt(l), +const u = nt(l), re = nt(Pn), pe = nt(In); function le(e) { @@ -62766,20 +56675,20 @@ const se = ie(rt), titleId: t, ...n } = _ref; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M5.0484 1.40838C6.12624 0.33054 7.87376 0.330541 8.9516 1.40838L12.5916 5.0484C13.6695 6.12624 13.6695 7.87376 12.5916 8.9516L8.9516 12.5916C7.87376 13.6695 6.12624 13.6695 5.0484 12.5916L1.40838 8.9516C0.33054 7.87376 0.330541 6.12624 1.40838 5.0484L5.0484 1.40838Z", stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("rect", { + }), u.createElement("rect", { x: 6, y: 6, width: 2, @@ -62794,16 +56703,16 @@ const se = ie(rt), titleId: t, ...n } = _ref2; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 14 9", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M1 1L7 7L13 1", stroke: "currentColor", strokeWidth: 1.5 @@ -62815,16 +56724,16 @@ const se = ie(rt), titleId: t, ...n } = _ref3; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 7 10", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M6 1.04819L2 5.04819L6 9.04819", stroke: "currentColor", strokeWidth: 1.75 @@ -62836,16 +56745,16 @@ const se = ie(rt), titleId: t, ...n } = _ref4; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 14 9", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M13 8L7 2L1 8", stroke: "currentColor", strokeWidth: 1.5 @@ -62857,20 +56766,20 @@ const se = ie(rt), titleId: t, ...n } = _ref5; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M1 1L12.9998 12.9997", stroke: "currentColor", strokeWidth: 1.5 - }), c.createElement("path", { + }), u.createElement("path", { d: "M13 1L1.00079 13.0003", stroke: "currentColor", strokeWidth: 1.5 @@ -62882,20 +56791,20 @@ const se = ie(rt), titleId: t, ...n } = _ref6; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "-2 -2 22 22", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M11.25 14.2105V15.235C11.25 16.3479 10.3479 17.25 9.23501 17.25H2.76499C1.65214 17.25 0.75 16.3479 0.75 15.235L0.75 8.76499C0.75 7.65214 1.65214 6.75 2.76499 6.75L3.78947 6.75", stroke: "currentColor", strokeWidth: 1.5 - }), c.createElement("rect", { + }), u.createElement("rect", { x: 6.75, y: .75, width: 10.5, @@ -62911,24 +56820,24 @@ const se = ie(rt), titleId: t, ...n } = _ref7; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M5.0484 1.40838C6.12624 0.33054 7.87376 0.330541 8.9516 1.40838L12.5916 5.0484C13.6695 6.12624 13.6695 7.87376 12.5916 8.9516L8.9516 12.5916C7.87376 13.6695 6.12624 13.6695 5.0484 12.5916L1.40838 8.9516C0.33054 7.87376 0.330541 6.12624 1.40838 5.0484L5.0484 1.40838Z", stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("path", { + }), u.createElement("path", { d: "M5 9L9 5", stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("path", { + }), u.createElement("path", { d: "M5 5L9 9", stroke: "currentColor", strokeWidth: 1.2 @@ -62940,24 +56849,24 @@ const se = ie(rt), titleId: t, ...n } = _ref8; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M4 8L8 4", stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("path", { + }), u.createElement("path", { d: "M4 4L8 8", stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("path", { + }), u.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M8.5 1.2H9C9.99411 1.2 10.8 2.00589 10.8 3V9C10.8 9.99411 9.99411 10.8 9 10.8H8.5V12H9C10.6569 12 12 10.6569 12 9V3C12 1.34315 10.6569 0 9 0H8.5V1.2ZM3.5 1.2V0H3C1.34315 0 0 1.34315 0 3V9C0 10.6569 1.34315 12 3 12H3.5V10.8H3C2.00589 10.8 1.2 9.99411 1.2 9V3C1.2 2.00589 2.00589 1.2 3 1.2H3.5Z", @@ -62970,16 +56879,16 @@ const se = ie(rt), titleId: t, ...n } = _ref9; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("rect", { + }, e) : null, u.createElement("rect", { x: .6, y: .6, width: 10.8, @@ -62987,11 +56896,11 @@ const se = ie(rt), rx: 3.4, stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("path", { + }), u.createElement("path", { d: "M4 8L8 4", stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("path", { + }), u.createElement("path", { d: "M4 4L8 8", stroke: "currentColor", strokeWidth: 1.2 @@ -63003,15 +56912,15 @@ const se = ie(rt), titleId: t, ...n } = _ref10; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0.5 12 12", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("rect", { + }, e) : null, u.createElement("rect", { x: 7, y: 5.5, width: 2, @@ -63019,7 +56928,7 @@ const se = ie(rt), rx: 1, transform: "rotate(90 7 5.5)", fill: "currentColor" - }), c.createElement("path", { + }), u.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M10.8 9L10.8 9.5C10.8 10.4941 9.99411 11.3 9 11.3L3 11.3C2.00589 11.3 1.2 10.4941 1.2 9.5L1.2 9L-3.71547e-07 9L-3.93402e-07 9.5C-4.65826e-07 11.1569 1.34314 12.5 3 12.5L9 12.5C10.6569 12.5 12 11.1569 12 9.5L12 9L10.8 9ZM10.8 4L12 4L12 3.5C12 1.84315 10.6569 0.5 9 0.5L3 0.5C1.34315 0.5 -5.87117e-08 1.84315 -1.31135e-07 3.5L-1.5299e-07 4L1.2 4L1.2 3.5C1.2 2.50589 2.00589 1.7 3 1.7L9 1.7C9.99411 1.7 10.8 2.50589 10.8 3.5L10.8 4Z", @@ -63032,24 +56941,24 @@ const se = ie(rt), titleId: t, ...n } = _ref11; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 20 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M0.75 3C0.75 1.75736 1.75736 0.75 3 0.75H17.25C17.8023 0.75 18.25 1.19772 18.25 1.75V5.25", stroke: "currentColor", strokeWidth: 1.5 - }), c.createElement("path", { + }), u.createElement("path", { d: "M0.75 3C0.75 4.24264 1.75736 5.25 3 5.25H18.25C18.8023 5.25 19.25 5.69771 19.25 6.25V22.25C19.25 22.8023 18.8023 23.25 18.25 23.25H3C1.75736 23.25 0.75 22.2426 0.75 21V3Z", stroke: "currentColor", strokeWidth: 1.5 - }), c.createElement("path", { + }), u.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M3 5.25C1.75736 5.25 0.75 4.24264 0.75 3V21C0.75 22.2426 1.75736 23.25 3 23.25H18.25C18.8023 23.25 19.25 22.8023 19.25 22.25V6.25C19.25 5.69771 18.8023 5.25 18.25 5.25H3ZM13 11L6 11V12.5L13 12.5V11Z", @@ -63062,20 +56971,20 @@ const se = ie(rt), titleId: t, ...n } = _ref12; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 20 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M0.75 3C0.75 4.24264 1.75736 5.25 3 5.25H17.25M0.75 3C0.75 1.75736 1.75736 0.75 3 0.75H16.25C16.8023 0.75 17.25 1.19772 17.25 1.75V5.25M0.75 3V21C0.75 22.2426 1.75736 23.25 3 23.25H18.25C18.8023 23.25 19.25 22.8023 19.25 22.25V6.25C19.25 5.69771 18.8023 5.25 18.25 5.25H17.25", stroke: "currentColor", strokeWidth: 1.5 - }), c.createElement("line", { + }), u.createElement("line", { x1: 13, y1: 11.75, x2: 6, @@ -63090,23 +56999,23 @@ const se = ie(rt), titleId: t, ...n } = _ref13; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("rect", { + }, e) : null, u.createElement("rect", { x: 5, y: 5, width: 2, height: 2, rx: 1, fill: "currentColor" - }), c.createElement("path", { + }), u.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M8.5 1.2H9C9.99411 1.2 10.8 2.00589 10.8 3V9C10.8 9.99411 9.99411 10.8 9 10.8H8.5V12H9C10.6569 12 12 10.6569 12 9V3C12 1.34315 10.6569 0 9 0H8.5V1.2ZM3.5 1.2V0H3C1.34315 0 0 1.34315 0 3V9C0 10.6569 1.34315 12 3 12H3.5V10.8H3C2.00589 10.8 1.2 9.99411 1.2 9V3C1.2 2.00589 2.00589 1.2 3 1.2H3.5Z", @@ -63119,16 +57028,16 @@ const se = ie(rt), titleId: t, ...n } = _ref14; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 12 13", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("rect", { + }, e) : null, u.createElement("rect", { x: .6, y: 1.1, width: 10.8, @@ -63136,7 +57045,7 @@ const se = ie(rt), rx: 2.4, stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("rect", { + }), u.createElement("rect", { x: 5, y: 5.5, width: 2, @@ -63151,26 +57060,26 @@ const se = ie(rt), titleId: t, ...n } = _ref15; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 24 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M1.59375 9.52344L4.87259 12.9944L8.07872 9.41249", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "square" - }), c.createElement("path", { + }), u.createElement("path", { d: "M13.75 5.25V10.75H18.75", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "square" - }), c.createElement("path", { + }), u.createElement("path", { d: "M4.95427 11.9332C4.55457 10.0629 4.74441 8.11477 5.49765 6.35686C6.25089 4.59894 7.5305 3.11772 9.16034 2.11709C10.7902 1.11647 12.6901 0.645626 14.5986 0.769388C16.5071 0.893151 18.3303 1.60543 19.8172 2.80818C21.3042 4.01093 22.3818 5.64501 22.9017 7.48548C23.4216 9.32595 23.3582 11.2823 22.7203 13.0853C22.0824 14.8883 20.9013 16.4492 19.3396 17.5532C17.778 18.6572 15.9125 19.25 14 19.25", stroke: "currentColor", strokeWidth: 1.5 @@ -63182,16 +57091,16 @@ const se = ie(rt), titleId: t, ...n } = _ref16; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("circle", { + }, e) : null, u.createElement("circle", { cx: 6, cy: 6, r: 5.4, @@ -63200,7 +57109,7 @@ const se = ie(rt), strokeDasharray: "4.241025 4.241025", transform: "rotate(22.5)", "transform-origin": "center" - }), c.createElement("circle", { + }), u.createElement("circle", { cx: 6, cy: 6, r: 1, @@ -63213,40 +57122,40 @@ const se = ie(rt), titleId: t, ...n } = _ref17; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 19 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M1.5 14.5653C1.5 15.211 1.75652 15.8303 2.21314 16.2869C2.66975 16.7435 3.28905 17 3.9348 17C4.58054 17 5.19984 16.7435 5.65646 16.2869C6.11307 15.8303 6.36959 15.211 6.36959 14.5653V12.1305H3.9348C3.28905 12.1305 2.66975 12.387 2.21314 12.8437C1.75652 13.3003 1.5 13.9195 1.5 14.5653Z", stroke: "currentColor", strokeWidth: 1.125, strokeLinecap: "round", strokeLinejoin: "round" - }), c.createElement("path", { + }), u.createElement("path", { d: "M3.9348 1.00063C3.28905 1.00063 2.66975 1.25715 2.21314 1.71375C1.75652 2.17035 1.5 2.78964 1.5 3.43537C1.5 4.0811 1.75652 4.70038 2.21314 5.15698C2.66975 5.61358 3.28905 5.8701 3.9348 5.8701H6.36959V3.43537C6.36959 2.78964 6.11307 2.17035 5.65646 1.71375C5.19984 1.25715 4.58054 1.00063 3.9348 1.00063Z", stroke: "currentColor", strokeWidth: 1.125, strokeLinecap: "round", strokeLinejoin: "round" - }), c.createElement("path", { + }), u.createElement("path", { d: "M15.0652 12.1305H12.6304V14.5653C12.6304 15.0468 12.7732 15.5175 13.0407 15.9179C13.3083 16.3183 13.6885 16.6304 14.1334 16.8147C14.5783 16.9989 15.0679 17.0472 15.5402 16.9532C16.0125 16.8593 16.4464 16.6274 16.7869 16.2869C17.1274 15.9464 17.3593 15.5126 17.4532 15.0403C17.5472 14.568 17.4989 14.0784 17.3147 13.6335C17.1304 13.1886 16.8183 12.8084 16.4179 12.5409C16.0175 12.2733 15.5468 12.1305 15.0652 12.1305Z", stroke: "currentColor", strokeWidth: 1.125, strokeLinecap: "round", strokeLinejoin: "round" - }), c.createElement("path", { + }), u.createElement("path", { d: "M12.6318 5.86775H6.36955V12.1285H12.6318V5.86775Z", stroke: "currentColor", strokeWidth: 1.125, strokeLinecap: "round", strokeLinejoin: "round" - }), c.createElement("path", { + }), u.createElement("path", { d: "M17.5 3.43473C17.5 2.789 17.2435 2.16972 16.7869 1.71312C16.3303 1.25652 15.711 1 15.0652 1C14.4195 1 13.8002 1.25652 13.3435 1.71312C12.8869 2.16972 12.6304 2.789 12.6304 3.43473V5.86946H15.0652C15.711 5.86946 16.3303 5.61295 16.7869 5.15635C17.2435 4.69975 17.5 4.08046 17.5 3.43473Z", stroke: "currentColor", strokeWidth: 1.125, @@ -63260,22 +57169,22 @@ const se = ie(rt), titleId: t, ...n } = _ref18; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 13 13", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("circle", { + }, e) : null, u.createElement("circle", { cx: 5, cy: 5, r: 4.35, stroke: "currentColor", strokeWidth: 1.3 - }), c.createElement("line", { + }), u.createElement("line", { x1: 8.45962, y1: 8.54038, x2: 11.7525, @@ -63290,28 +57199,28 @@ const se = ie(rt), titleId: t, ...n } = _ref19; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "-2 -2 22 22", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M17.2492 6V2.9569C17.2492 1.73806 16.2611 0.75 15.0423 0.75L2.9569 0.75C1.73806 0.75 0.75 1.73806 0.75 2.9569L0.75 6", stroke: "currentColor", strokeWidth: 1.5 - }), c.createElement("path", { + }), u.createElement("path", { d: "M0.749873 12V15.0431C0.749873 16.2619 1.73794 17.25 2.95677 17.25H15.0421C16.261 17.25 17.249 16.2619 17.249 15.0431V12", stroke: "currentColor", strokeWidth: 1.5 - }), c.createElement("path", { + }), u.createElement("path", { d: "M6 4.5L9 7.5L12 4.5", stroke: "currentColor", strokeWidth: 1.5 - }), c.createElement("path", { + }), u.createElement("path", { d: "M12 13.5L9 10.5L6 13.5", stroke: "currentColor", strokeWidth: 1.5 @@ -63323,25 +57232,25 @@ const se = ie(rt), titleId: t, ...n } = _ref20; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M0.75 13.25L0.0554307 12.967C-0.0593528 13.2488 0.00743073 13.5719 0.224488 13.7851C0.441545 13.9983 0.765869 14.0592 1.04549 13.9393L0.75 13.25ZM12.8214 1.83253L12.2911 2.36286L12.2911 2.36286L12.8214 1.83253ZM12.8214 3.90194L13.3517 4.43227L12.8214 3.90194ZM10.0981 1.17859L9.56773 0.648259L10.0981 1.17859ZM12.1675 1.17859L12.6978 0.648258L12.6978 0.648257L12.1675 1.17859ZM2.58049 8.75697L3.27506 9.03994L2.58049 8.75697ZM2.70066 8.57599L3.23099 9.10632L2.70066 8.57599ZM5.2479 11.4195L4.95355 10.7297L5.2479 11.4195ZM5.42036 11.303L4.89003 10.7727L5.42036 11.303ZM4.95355 10.7297C4.08882 11.0987 3.41842 11.362 2.73535 11.6308C2.05146 11.9 1.35588 12.1743 0.454511 12.5607L1.04549 13.9393C1.92476 13.5624 2.60256 13.2951 3.28469 13.0266C3.96762 12.7578 4.65585 12.4876 5.54225 12.1093L4.95355 10.7297ZM1.44457 13.533L3.27506 9.03994L1.88592 8.474L0.0554307 12.967L1.44457 13.533ZM3.23099 9.10632L10.6284 1.70892L9.56773 0.648259L2.17033 8.04566L3.23099 9.10632ZM11.6371 1.70892L12.2911 2.36286L13.3517 1.3022L12.6978 0.648258L11.6371 1.70892ZM12.2911 3.37161L4.89003 10.7727L5.95069 11.8333L13.3517 4.43227L12.2911 3.37161ZM12.2911 2.36286C12.5696 2.64142 12.5696 3.09305 12.2911 3.37161L13.3517 4.43227C14.2161 3.56792 14.2161 2.16654 13.3517 1.3022L12.2911 2.36286ZM10.6284 1.70892C10.9069 1.43036 11.3586 1.43036 11.6371 1.70892L12.6978 0.648257C11.8335 -0.216088 10.4321 -0.216084 9.56773 0.648259L10.6284 1.70892ZM3.27506 9.03994C3.26494 9.06479 3.24996 9.08735 3.23099 9.10632L2.17033 8.04566C2.04793 8.16806 1.95123 8.31369 1.88592 8.474L3.27506 9.03994ZM5.54225 12.1093C5.69431 12.0444 5.83339 11.9506 5.95069 11.8333L4.89003 10.7727C4.90863 10.7541 4.92988 10.7398 4.95355 10.7297L5.54225 12.1093Z", fill: "currentColor" - }), c.createElement("path", { + }), u.createElement("path", { d: "M11.5 4.5L9.5 2.5", stroke: "currentColor", strokeWidth: 1.4026, strokeLinecap: "round", strokeLinejoin: "round" - }), c.createElement("path", { + }), u.createElement("path", { d: "M5.5 10.5L3.5 8.5", stroke: "currentColor", strokeWidth: 1.4026, @@ -63355,16 +57264,16 @@ const se = ie(rt), titleId: t, ...n } = _ref21; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 16 18", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M1.32226e-07 1.6609C7.22332e-08 0.907329 0.801887 0.424528 1.46789 0.777117L15.3306 8.11621C16.0401 8.49182 16.0401 9.50818 15.3306 9.88379L1.46789 17.2229C0.801886 17.5755 1.36076e-06 17.0927 1.30077e-06 16.3391L1.32226e-07 1.6609Z", fill: "currentColor" })); @@ -63375,16 +57284,16 @@ const se = ie(rt), titleId: t, ...n } = _ref22; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 10 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4.25 9.25V13.5H5.75V9.25L10 9.25V7.75L5.75 7.75V3.5H4.25V7.75L0 7.75V9.25L4.25 9.25Z", @@ -63397,7 +57306,7 @@ const se = ie(rt), titleId: t, ...n } = _ref23; - return c.createElement("svg", { + return u.createElement("svg", { width: 25, height: 25, viewBox: "0 0 25 25", @@ -63405,26 +57314,26 @@ const se = ie(rt), xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M10.2852 24.0745L13.7139 18.0742", stroke: "currentColor", strokeWidth: 1.5625 - }), c.createElement("path", { + }), u.createElement("path", { d: "M14.5742 24.0749L17.1457 19.7891", stroke: "currentColor", strokeWidth: 1.5625 - }), c.createElement("path", { + }), u.createElement("path", { d: "M19.4868 24.0735L20.7229 21.7523C21.3259 20.6143 21.5457 19.3122 21.3496 18.0394C21.1535 16.7666 20.5519 15.591 19.6342 14.6874L23.7984 6.87853C24.0123 6.47728 24.0581 6.00748 23.9256 5.57249C23.7932 5.1375 23.4933 4.77294 23.0921 4.55901C22.6908 4.34509 22.221 4.29932 21.7861 4.43178C21.3511 4.56424 20.9865 4.86408 20.7726 5.26533L16.6084 13.0742C15.3474 12.8142 14.0362 12.9683 12.8699 13.5135C11.7035 14.0586 10.7443 14.9658 10.135 16.1L6 24.0735", stroke: "currentColor", strokeWidth: 1.5625 - }), c.createElement("path", { + }), u.createElement("path", { d: "M4 15L5 13L7 12L5 11L4 9L3 11L1 12L3 13L4 15Z", stroke: "currentColor", strokeWidth: 1.5625, strokeLinejoin: "round" - }), c.createElement("path", { + }), u.createElement("path", { d: "M11.5 8L12.6662 5.6662L15 4.5L12.6662 3.3338L11.5 1L10.3338 3.3338L8 4.5L10.3338 5.6662L11.5 8Z", stroke: "currentColor", strokeWidth: 1.5625, @@ -63437,30 +57346,30 @@ const se = ie(rt), titleId: t, ...n } = _ref24; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M4.75 9.25H1.25V12.75", stroke: "currentColor", strokeWidth: 1, strokeLinecap: "square" - }), c.createElement("path", { + }), u.createElement("path", { d: "M11.25 6.75H14.75V3.25", stroke: "currentColor", strokeWidth: 1, strokeLinecap: "square" - }), c.createElement("path", { + }), u.createElement("path", { d: "M14.1036 6.65539C13.8 5.27698 13.0387 4.04193 11.9437 3.15131C10.8487 2.26069 9.48447 1.76694 8.0731 1.75043C6.66173 1.73392 5.28633 2.19563 4.17079 3.0604C3.05526 3.92516 2.26529 5.14206 1.92947 6.513", stroke: "currentColor", strokeWidth: 1 - }), c.createElement("path", { + }), u.createElement("path", { d: "M1.89635 9.34461C2.20001 10.723 2.96131 11.9581 4.05631 12.8487C5.15131 13.7393 6.51553 14.2331 7.9269 14.2496C9.33827 14.2661 10.7137 13.8044 11.8292 12.9396C12.9447 12.0748 13.7347 10.8579 14.0705 9.487", stroke: "currentColor", strokeWidth: 1 @@ -63472,16 +57381,16 @@ const se = ie(rt), titleId: t, ...n } = _ref25; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 13 13", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("rect", { + }, e) : null, u.createElement("rect", { x: .6, y: .6, width: 11.8, @@ -63489,7 +57398,7 @@ const se = ie(rt), rx: 5.9, stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("path", { + }), u.createElement("path", { d: "M4.25 7.5C4.25 6 5.75 5 6.5 6.5C7.25 8 8.75 7 8.75 5.5", stroke: "currentColor", strokeWidth: 1.2 @@ -63501,16 +57410,16 @@ const se = ie(rt), titleId: t, ...n } = _ref26; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 21 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M9.29186 1.92702C9.06924 1.82745 8.87014 1.68202 8.70757 1.50024L7.86631 0.574931C7.62496 0.309957 7.30773 0.12592 6.95791 0.0479385C6.60809 -0.0300431 6.24274 0.00182978 5.91171 0.139208C5.58068 0.276585 5.3001 0.512774 5.10828 0.815537C4.91645 1.1183 4.82272 1.47288 4.83989 1.83089L4.90388 3.08019C4.91612 3.32348 4.87721 3.56662 4.78968 3.79394C4.70215 4.02126 4.56794 4.2277 4.39571 4.39994C4.22347 4.57219 4.01704 4.7064 3.78974 4.79394C3.56243 4.88147 3.3193 4.92038 3.07603 4.90814L1.8308 4.84414C1.47162 4.82563 1.11553 4.91881 0.811445 5.11086C0.507359 5.30292 0.270203 5.58443 0.132561 5.91671C-0.00508149 6.249 -0.0364554 6.61576 0.0427496 6.9666C0.121955 7.31744 0.307852 7.63514 0.5749 7.87606L1.50016 8.71204C1.68193 8.87461 1.82735 9.07373 1.92692 9.29636C2.02648 9.51898 2.07794 9.76012 2.07794 10.004C2.07794 10.2479 2.02648 10.489 1.92692 10.7116C1.82735 10.9343 1.68193 11.1334 1.50016 11.296L0.5749 12.1319C0.309856 12.3729 0.125575 12.6898 0.0471809 13.0393C-0.0312128 13.3888 9.64098e-05 13.754 0.13684 14.0851C0.273583 14.4162 0.509106 14.6971 0.811296 14.8894C1.11349 15.0817 1.46764 15.1762 1.82546 15.1599L3.0707 15.0959C3.31397 15.0836 3.5571 15.1225 3.7844 15.2101C4.01171 15.2976 4.21814 15.4318 4.39037 15.6041C4.56261 15.7763 4.69682 15.9827 4.78435 16.2101C4.87188 16.4374 4.91078 16.6805 4.89855 16.9238L4.83455 18.1691C4.81605 18.5283 4.90921 18.8844 5.10126 19.1885C5.2933 19.4926 5.5748 19.7298 5.90707 19.8674C6.23934 20.0051 6.60608 20.0365 6.9569 19.9572C7.30772 19.878 7.6254 19.6921 7.86631 19.4251L8.7129 18.4998C8.87547 18.318 9.07458 18.1725 9.29719 18.073C9.51981 17.9734 9.76093 17.9219 10.0048 17.9219C10.2487 17.9219 10.4898 17.9734 10.7124 18.073C10.935 18.1725 11.1341 18.318 11.2967 18.4998L12.1326 19.4251C12.3735 19.6921 12.6912 19.878 13.042 19.9572C13.3929 20.0365 13.7596 20.0051 14.0919 19.8674C14.4241 19.7298 14.7056 19.4926 14.8977 19.1885C15.0897 18.8844 15.1829 18.5283 15.1644 18.1691L15.1004 16.9238C15.0882 16.6805 15.1271 16.4374 15.2146 16.2101C15.3021 15.9827 15.4363 15.7763 15.6086 15.6041C15.7808 15.4318 15.9872 15.2976 16.2145 15.2101C16.4418 15.1225 16.685 15.0836 16.9282 15.0959L18.1735 15.1599C18.5326 15.1784 18.8887 15.0852 19.1928 14.8931C19.4969 14.7011 19.7341 14.4196 19.8717 14.0873C20.0093 13.755 20.0407 13.3882 19.9615 13.0374C19.8823 12.6866 19.6964 12.3689 19.4294 12.1279L18.5041 11.292C18.3223 11.1294 18.1769 10.9303 18.0774 10.7076C17.9778 10.485 17.9263 10.2439 17.9263 10C17.9263 9.75612 17.9778 9.51499 18.0774 9.29236C18.1769 9.06973 18.3223 8.87062 18.5041 8.70804L19.4294 7.87206C19.6964 7.63114 19.8823 7.31344 19.9615 6.9626C20.0407 6.61176 20.0093 6.245 19.8717 5.91271C19.7341 5.58043 19.4969 5.29892 19.1928 5.10686C18.8887 4.91481 18.5326 4.82163 18.1735 4.84014L16.9282 4.90414C16.685 4.91638 16.4418 4.87747 16.2145 4.78994C15.9872 4.7024 15.7808 4.56818 15.6086 4.39594C15.4363 4.2237 15.3021 4.01726 15.2146 3.78994C15.1271 3.56262 15.0882 3.31948 15.1004 3.07619L15.1644 1.83089C15.1829 1.4717 15.0897 1.11559 14.8977 0.811487C14.7056 0.507385 14.4241 0.270217 14.0919 0.132568C13.7596 -0.00508182 13.3929 -0.0364573 13.042 0.0427519C12.6912 0.121961 12.3735 0.307869 12.1326 0.574931L11.2914 1.50024C11.1288 1.68202 10.9297 1.82745 10.7071 1.92702C10.4845 2.02659 10.2433 2.07805 9.99947 2.07805C9.7556 2.07805 9.51448 2.02659 9.29186 1.92702ZM14.3745 10C14.3745 12.4162 12.4159 14.375 9.99977 14.375C7.58365 14.375 5.625 12.4162 5.625 10C5.625 7.58375 7.58365 5.625 9.99977 5.625C12.4159 5.625 14.3745 7.58375 14.3745 10Z", @@ -63523,16 +57432,16 @@ const se = ie(rt), titleId: t, ...n } = _ref27; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M6.5782 1.07092C6.71096 0.643026 7.28904 0.643027 7.4218 1.07092L8.59318 4.84622C8.65255 5.03758 8.82284 5.16714 9.01498 5.16714L12.8056 5.16714C13.2353 5.16714 13.4139 5.74287 13.0663 6.00732L9.99962 8.34058C9.84418 8.45885 9.77913 8.66848 9.83851 8.85984L11.0099 12.6351C11.1426 13.063 10.675 13.4189 10.3274 13.1544L7.26069 10.8211C7.10524 10.7029 6.89476 10.7029 6.73931 10.8211L3.6726 13.1544C3.32502 13.4189 2.85735 13.063 2.99012 12.6351L4.16149 8.85984C4.22087 8.66848 4.15582 8.45885 4.00038 8.34058L0.933671 6.00732C0.586087 5.74287 0.764722 5.16714 1.19436 5.16714L4.98502 5.16714C5.17716 5.16714 5.34745 5.03758 5.40682 4.84622L6.5782 1.07092Z", fill: "currentColor", stroke: "currentColor" @@ -63544,16 +57453,16 @@ const se = ie(rt), titleId: t, ...n } = _ref28; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M6.5782 1.07092C6.71096 0.643026 7.28904 0.643027 7.4218 1.07092L8.59318 4.84622C8.65255 5.03758 8.82284 5.16714 9.01498 5.16714L12.8056 5.16714C13.2353 5.16714 13.4139 5.74287 13.0663 6.00732L9.99962 8.34058C9.84418 8.45885 9.77913 8.66848 9.83851 8.85984L11.0099 12.6351C11.1426 13.063 10.675 13.4189 10.3274 13.1544L7.26069 10.8211C7.10524 10.7029 6.89476 10.7029 6.73931 10.8211L3.6726 13.1544C3.32502 13.4189 2.85735 13.063 2.99012 12.6351L4.16149 8.85984C4.22087 8.66848 4.15582 8.45885 4.00038 8.34058L0.933671 6.00732C0.586087 5.74287 0.764722 5.16714 1.19436 5.16714L4.98502 5.16714C5.17716 5.16714 5.34745 5.03758 5.40682 4.84622L6.5782 1.07092Z", stroke: "currentColor", strokeWidth: 1.5 @@ -63565,16 +57474,16 @@ const se = ie(rt), titleId: t, ...n } = _ref29; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("rect", { + }, e) : null, u.createElement("rect", { width: 16, height: 16, rx: 2, @@ -63587,7 +57496,7 @@ const se = ie(rt), titleId: t, ...n } = _ref30; - return c.createElement("svg", { + return u.createElement("svg", { width: "1em", height: "5em", xmlns: "http://www.w3.org/2000/svg", @@ -63600,11 +57509,11 @@ const se = ie(rt), clipRule: "evenodd", "aria-labelledby": t, ...n - }, e === void 0 ? c.createElement("title", { + }, e === void 0 ? u.createElement("title", { id: t - }, "trash icon") : e ? c.createElement("title", { + }, "trash icon") : e ? u.createElement("title", { id: t - }, e) : null, c.createElement("path", { + }, e) : null, u.createElement("path", { d: "M19 24h-14c-1.104 0-2-.896-2-2v-17h-1v-2h6v-1.5c0-.827.673-1.5 1.5-1.5h5c.825 0 1.5.671 1.5 1.5v1.5h6v2h-1v17c0 1.104-.896 2-2 2zm0-19h-14v16.5c0 .276.224.5.5.5h13c.276 0 .5-.224.5-.5v-16.5zm-7 7.586l3.293-3.293 1.414 1.414-3.293 3.293 3.293 3.293-1.414 1.414-3.293-3.293-3.293 3.293-1.414-1.414 3.293-3.293-3.293-3.293 1.414-1.414 3.293 3.293zm2-10.586h-4v1h4v-1z", fill: "currentColor", strokeWidth: .25, @@ -63617,16 +57526,16 @@ const se = ie(rt), titleId: t, ...n } = _ref31; - return c.createElement("svg", { + return u.createElement("svg", { height: "1em", viewBox: "0 0 13 13", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-labelledby": t, ...n - }, e ? c.createElement("title", { + }, e ? u.createElement("title", { id: t - }, e) : null, c.createElement("rect", { + }, e) : null, u.createElement("rect", { x: .6, y: .6, width: 11.8, @@ -63634,7 +57543,7 @@ const se = ie(rt), rx: 5.9, stroke: "currentColor", strokeWidth: 1.2 - }), c.createElement("rect", { + }), u.createElement("rect", { x: 5.5, y: 5.5, width: 2, @@ -63842,7 +57751,7 @@ a(en, "TooltipRoot"); const J = ye(en, { Provider: pe.Provider }); -const tn = l.forwardRef((_ref38, u) => { +const tn = l.forwardRef((_ref38, c) => { let { isActive: e, value: t, @@ -63852,7 +57761,7 @@ const tn = l.forwardRef((_ref38, u) => { } = _ref38; return r.jsx(Nt.Reorder.Item, { ...o, - ref: u, + ref: c, value: t, "aria-selected": e ? "true" : void 0, role: "tab", @@ -63885,7 +57794,7 @@ const Lr = ye(tn, { Button: nn, Close: rn }), - sn = l.forwardRef((_ref39, u) => { + sn = l.forwardRef((_ref39, c) => { let { values: e, onReorder: t, @@ -63895,7 +57804,7 @@ const Lr = ye(tn, { } = _ref39; return r.jsx(Nt.Reorder.Group, { ...o, - ref: u, + ref: c, values: e, onReorder: t, axis: "x", @@ -63911,7 +57820,7 @@ function on(e) { const t = se(), n = l.useRef(new B.HistoryStore(t || new B.StorageAPI(null), e.maxHistoryLength || jr)), [s, o] = l.useState(((y = n.current) == null ? void 0 : y.queries) || []), - u = l.useCallback(h => { + c = l.useCallback(h => { var x; (x = n.current) == null || x.updateHistory(h), o(n.current.queries); }, []), @@ -63927,13 +57836,13 @@ function on(e) { n.current.deleteHistory(h, x), o(n.current.queries); }, []), p = l.useMemo(() => ({ - addToHistory: u, + addToHistory: c, editLabel: i, items: s, toggleFavorite: d, setActive: g, deleteFromHistory: m - }), [u, i, s, d, g, m]); + }), [c, i, s, d, g, m]); return r.jsx(ot.Provider, { value: p, children: e.children @@ -63955,18 +57864,18 @@ function ln() { })).reverse(); const s = n.filter(d => d.favorite); s.length && (n = n.filter(d => !d.favorite)); - const [o, u] = l.useState(null); + const [o, c] = l.useState(null); l.useEffect(() => { o && setTimeout(() => { - u(null); + c(null); }, 2e3); }, [o]); const i = l.useCallback(() => { try { for (const d of n) t(d, !0); - u("success"); + c("success"); } catch { - u("error"); + c("error"); } }, [t, n]); return r.jsxs("section", { @@ -64011,7 +57920,7 @@ function Pe(e) { caller: Pe }), { - headerEditor: u, + headerEditor: c, queryEditor: i, variableEditor: d } = Z({ @@ -64050,8 +57959,8 @@ function Pe(e) { variables: w, headers: T } = e.item; - i == null || i.setValue(b !== null && b !== void 0 ? b : ""), d == null || d.setValue(w !== null && w !== void 0 ? w : ""), u == null || u.setValue(T !== null && T !== void 0 ? T : ""), o(e.item); - }, [u, e.item, i, o, d]), + i == null || i.setValue(b !== null && b !== void 0 ? b : ""), d == null || d.setValue(w !== null && w !== void 0 ? w : ""), c == null || c.setValue(T !== null && T !== void 0 ? T : ""), o(e.item); + }, [c, e.item, i, o, d]), k = l.useCallback(b => { b.stopPropagation(), s(e.item); }, [e.item, s]), @@ -64149,7 +58058,7 @@ function qe(_ref40) { if (!e) throw new TypeError("The `ExecutionContextProvider` component requires a `fetcher` function to be passed as prop."); const { externalFragments: o, - headerEditor: u, + headerEditor: c, queryEditor: i, responseEditor: d, variableEditor: g, @@ -64196,7 +58105,7 @@ function qe(_ref40) { T(N instanceof Error ? N.message : `${N}`); return; } - const O = u == null ? void 0 : u.getValue(); + const O = c == null ? void 0 : c.getValue(); let D; try { D = bt({ @@ -64240,7 +58149,7 @@ function qe(_ref40) { const { path: vt, data: Le, - errors: hs, + errors: ms, ...Nn } = z; if (vt) { @@ -64292,7 +58201,7 @@ function qe(_ref40) { } catch (N) { x(!1), T(B.formatError(N)), C(null); } - }, [y, o, e, u, p, s, i, d, k, f, m, g]), + }, [y, o, e, c, p, s, i, d, k, f, m, g]), b = !!f, w = l.useMemo(() => ({ isFetching: h, @@ -64317,8 +58226,8 @@ function bt(_ref42) { let s; try { s = e && e.trim() !== "" ? JSON.parse(e) : void 0; - } catch (u) { - throw new Error(`${t}: ${u instanceof Error ? u.message : u}.`); + } catch (c) { + throw new Error(`${t}: ${c instanceof Error ? c.message : c}.`); } const o = typeof s == "object" && s !== null && !Array.isArray(s); if (s !== void 0 && !o) throw new Error(n); @@ -64369,7 +58278,7 @@ function ct(e) { caller: ct }), [s, o] = l.useState(), - [u, i] = l.useState(!1), + [c, i] = l.useState(!1), [d, g] = l.useState(null), m = l.useRef(0); l.useEffect(() => { @@ -64453,10 +58362,10 @@ function ct(e) { w = l.useMemo(() => ({ fetchError: d, introspect: L, - isFetching: u, + isFetching: c, schema: s, validationErrors: b - }), [d, L, u, s, b]); + }), [d, L, c, s, b]); return r.jsx(at.Provider, { value: w, children: k @@ -64477,11 +58386,11 @@ function Tr(_ref44) { schemaDescription: n }); t && (o = o.replace("query IntrospectionQuery", `query ${s}`)); - const u = o.replace("subscriptionType { name }", ""); + const c = o.replace("subscriptionType { name }", ""); return { introspectionQueryName: s, introspectionQuery: o, - introspectionQuerySansSubscriptions: u + introspectionQuerySansSubscriptions: c }; }, [e, t, n]); } @@ -64513,7 +58422,7 @@ function dt(e) { caller: dt }), [s, o] = l.useState([je]), - u = l.useCallback(m => { + c = l.useCallback(m => { o(p => p.at(-1).def === m.def ? p : [...p, m]); }, []), i = l.useCallback(() => { @@ -64559,10 +58468,10 @@ function dt(e) { }, [d, t, n]); const g = l.useMemo(() => ({ explorerNavStack: s, - push: u, + push: c, pop: i, reset: d - }), [s, u, i, d]); + }), [s, c, i, d]); return r.jsx(ut.Provider, { value: g, children: e.children @@ -64716,17 +58625,17 @@ function Pr(_ref47) { }, []); if (!("args" in e)) return null; const o = [], - u = []; - for (const i of e.args) i.deprecationReason ? u.push(i) : o.push(i); + c = []; + for (const i of e.args) i.deprecationReason ? c.push(i) : o.push(i); return r.jsxs(r.Fragment, { children: [o.length > 0 ? r.jsx(G, { title: "Arguments", children: o.map(i => r.jsx(Ce, { arg: i }, i.name)) - }) : null, u.length > 0 ? t || o.length === 0 ? r.jsx(G, { + }) : null, c.length > 0 ? t || o.length === 0 ? r.jsx(G, { title: "Deprecated Arguments", - children: u.map(i => r.jsx(Ce, { + children: c.map(i => r.jsx(Ce, { arg: i }, i.name)) }) : r.jsx(me, { @@ -64759,7 +58668,7 @@ function dn(e) { n = (d = (i = e.schema).getMutationType) == null ? void 0 : d.call(i), s = (m = (g = e.schema).getSubscriptionType) == null ? void 0 : m.call(g), o = e.schema.getTypeMap(), - u = [t == null ? void 0 : t.name, n == null ? void 0 : n.name, s == null ? void 0 : s.name]; + c = [t == null ? void 0 : t.name, n == null ? void 0 : n.name, s == null ? void 0 : s.name]; return r.jsxs(r.Fragment, { children: [r.jsx(K, { type: "description", @@ -64791,7 +58700,7 @@ function dn(e) { }), r.jsx(G, { title: "All Schema Types", children: o && r.jsx("div", { - children: Object.values(o).map(p => u.includes(p.name) || p.name.startsWith("__") ? null : r.jsx("div", { + children: Object.values(o).map(p => c.includes(p.name) || p.name.startsWith("__") ? null : r.jsx("div", { children: r.jsx(U, { type: p }) @@ -64823,7 +58732,7 @@ function mt() { }), n = l.useRef(null), s = Ke(), - [o, u] = l.useState(""), + [o, c] = l.useState(""), [i, d] = l.useState(s(o)), g = l.useMemo(() => ue(200, f => { d(s(f)); @@ -64867,7 +58776,7 @@ function mt() { autoComplete: "off", onFocus: h, onBlur: h, - onChange: f => u(f.target.value), + onChange: f => c(f.target.value), placeholder: "⌘ K", ref: n, value: o, @@ -64923,20 +58832,20 @@ function Ke(e) { }), s = t.at(-1); return l.useCallback(o => { - const u = { + const c = { within: [], types: [], fields: [] }; - if (!n) return u; + if (!n) return c; const i = s.def, d = n.getTypeMap(); let g = Object.keys(d); i && (g = g.filter(m => m !== i.name), g.unshift(i.name)); for (const m of g) { - if (u.within.length + u.types.length + u.fields.length >= 100) break; + if (c.within.length + c.types.length + c.fields.length >= 100) break; const p = d[m]; - if (i !== p && Qe(m, o) && u.types.push({ + if (i !== p && Qe(m, o) && c.types.push({ type: p }), !M.isObjectType(p) && !M.isInterfaceType(p) && !M.isInputObjectType(p)) continue; const y = p.getFields(); @@ -64946,7 +58855,7 @@ function Ke(e) { if (!Qe(h, o)) if ("args" in x) { if (f = x.args.filter(C => Qe(C.name, o)), f.length === 0) continue; } else continue; - u[i === p ? "within" : "fields"].push(...(f ? f.map(C => ({ + c[i === p ? "within" : "fields"].push(...(f ? f.map(C => ({ type: p, field: x, argument: C @@ -64956,7 +58865,7 @@ function Ke(e) { }])); } } - return u; + return c; }, [s.def, n]); } a(Ke, "useSearchResults"); @@ -65056,16 +58965,16 @@ function Ir(_ref51) { }, []); if (!M.isObjectType(e) && !M.isInterfaceType(e) && !M.isInputObjectType(e)) return null; const o = e.getFields(), - u = [], + c = [], i = []; - for (const d of Object.keys(o).map(g => o[g])) d.deprecationReason ? i.push(d) : u.push(d); + for (const d of Object.keys(o).map(g => o[g])) d.deprecationReason ? i.push(d) : c.push(d); return r.jsxs(r.Fragment, { - children: [u.length > 0 ? r.jsx(G, { + children: [c.length > 0 ? r.jsx(G, { title: "Fields", - children: u.map(d => r.jsx(Et, { + children: c.map(d => r.jsx(Et, { field: d }, d.name)) - }) : null, i.length > 0 ? t || u.length === 0 ? r.jsx(G, { + }) : null, i.length > 0 ? t || c.length === 0 ? r.jsx(G, { title: "Deprecated Fields", children: i.map(d => r.jsx(Et, { field: d @@ -65126,17 +59035,17 @@ function Hr(_ref53) { }, []); if (!M.isEnumType(e)) return null; const o = [], - u = []; - for (const i of e.getValues()) i.deprecationReason ? u.push(i) : o.push(i); + c = []; + for (const i of e.getValues()) i.deprecationReason ? c.push(i) : o.push(i); return r.jsxs(r.Fragment, { children: [o.length > 0 ? r.jsx(G, { title: "Enum Values", children: o.map(i => r.jsx(St, { value: i }, i.name)) - }) : null, u.length > 0 ? t || o.length === 0 ? r.jsx(G, { + }) : null, c.length > 0 ? t || o.length === 0 ? r.jsx(G, { title: "Deprecated Enum Values", - children: u.map(i => r.jsx(St, { + children: c.map(i => r.jsx(St, { value: i }, i.name)) }) : r.jsx(me, { @@ -65197,7 +59106,7 @@ function Ie() { }), { explorerNavStack: o, - pop: u + pop: c } = te({ nonNull: !0, caller: Ie @@ -65232,7 +59141,7 @@ function Ie() { href: "#", className: "graphiql-doc-explorer-back", onClick: m => { - m.preventDefault(), u(); + m.preventDefault(), c(); }, "aria-label": `Go back to ${g}`, children: [r.jsx(Rt, {}), g] @@ -65267,18 +59176,18 @@ function fn(e) { n = te(), s = be(), o = !!n, - u = !!s, + c = !!s, i = l.useMemo(() => { const x = [], f = {}; - o && (x.push(de), f[de.title] = !0), u && (x.push(Ye), f[Ye.title] = !0); + o && (x.push(de), f[de.title] = !0), c && (x.push(Ye), f[Ye.title] = !0); for (const C of e.plugins || []) { if (typeof C.title != "string" || !C.title) throw new Error("All GraphiQL plugins must have a unique title"); if (f[C.title]) throw new Error(`All GraphiQL plugins must have a unique title, found two plugins with the title '${C.title}'`); x.push(C), f[C.title] = !0; } return x; - }, [o, u, e.plugins]), + }, [o, c, e.plugins]), [d, g] = l.useState(() => { const x = t == null ? void 0 : t.get(Lt), f = i.find(C => C.title === x); @@ -65308,7 +59217,7 @@ function fn(e) { a(fn, "PluginContextProvider"); const Ze = ie(ft), Lt = "visiblePlugin"; -function Ar(e, t, n, s, o, u) { +function Ar(e, t, n, s, o, c) { Ee([], { useCommonAddons: !1 }).then(d => { @@ -65349,7 +59258,7 @@ function Ar(e, t, n, s, o, u) { m && (o.setVisiblePlugin(de), s.push({ name: m.name, def: m - }), u == null || u(m)); + }), c == null || c(m)); } a(i, "onClickHintInformation"); } @@ -65368,7 +59277,7 @@ function $e(e, t, n) { a($e, "useSynchronizeOption"); function pn(e, t, n, s, o) { const { - updateActiveTabValues: u + updateActiveTabValues: c } = Z({ nonNull: !0, caller: o @@ -65380,7 +59289,7 @@ function pn(e, t, n, s, o) { !i || n === null || i.set(n, p); }), g = ue(100, p => { - u({ + c({ [s]: p }); }), @@ -65390,7 +59299,7 @@ function pn(e, t, n, s, o) { d(h), g(h), t == null || t(h); }, "handleChange"); return e.on("change", m), () => e.off("change", m); - }, [t, e, i, n, s, u]); + }, [t, e, i, n, s, c]); } a(pn, "useChangeHandler"); function gn(e, t, n) { @@ -65401,11 +59310,11 @@ function gn(e, t, n) { caller: n }), o = te(), - u = Ze(); + c = Ze(); l.useEffect(() => { if (!e) return; const i = a((d, g) => { - Ar(d, g, s, o, u, m => { + Ar(d, g, s, o, c, m => { t == null || t({ kind: "Type", type: m, @@ -65414,7 +59323,7 @@ function gn(e, t, n) { }); }, "handleCompletion"); return e.on("hasCompletion", i), () => e.off("hasCompletion", i); - }, [t, e, o, u, s]); + }, [t, e, o, c, s]); } a(gn, "useCompletion"); function Y(e, t, n) { @@ -65487,21 +59396,21 @@ function Se() { if (s) { const o = s.getValue(); try { - const u = JSON.stringify(JSON.parse(o), null, 2); - u !== o && s.setValue(u); + const c = JSON.stringify(JSON.parse(o), null, 2); + c !== o && s.setValue(c); } catch {} } if (n) { const o = n.getValue(); try { - const u = JSON.stringify(JSON.parse(o), null, 2); - u !== o && n.setValue(u); + const c = JSON.stringify(JSON.parse(o), null, 2); + c !== o && n.setValue(c); } catch {} } if (t) { const o = t.getValue(), - u = M.print(M.parse(o)); - u !== o && t.setValue(u); + c = M.print(M.parse(o)); + c !== o && t.setValue(c); } }, [t, s, n]); } @@ -65527,15 +59436,15 @@ function He() { if (!s) return; const o = s.getValue(), { - insertions: u, + insertions: c, result: i } = B.fillLeafs(n, o, e); - return u && u.length > 0 && s.operation(() => { + return c && c.length > 0 && s.operation(() => { const d = s.getCursor(), g = s.indexFromPos(d); s.setValue(i || ""); let m = 0; - const p = u.map(_ref56 => { + const p = c.map(_ref56 => { let { index: h, string: x @@ -65553,7 +59462,7 @@ function He() { for (const { index: h, string: x - } of u) h < g && (y += x.length); + } of c) h < g && (y += x.length); s.setCursor(s.posFromIndex(y)); }), i; }, [e, s, n]); @@ -65567,12 +59476,28 @@ const Ge = a(e => { let s = ""; const o = (_ref57 = n == null ? void 0 : n.getValue()) !== null && _ref57 !== void 0 ? _ref57 : !1; o && o.length > 0 && (s = o); - const u = l.useCallback(i => n == null ? void 0 : n.setValue(i), [n]); - return l.useMemo(() => [s, u], [s, u]); + const c = l.useCallback(i => n == null ? void 0 : n.setValue(i), [n]); + return l.useMemo(() => [s, c], [s, c]); }, "useEditorState"), Or = a(() => Ge("query"), "useOperationsEditorState"), Fr = a(() => Ge("variable"), "useVariablesEditorState"), Br = a(() => Ge("header"), "useHeadersEditorState"); +function Wr(_ref58) { + let [e, t] = _ref58; + const n = l.useRef({ + pending: null, + last: e + }), + [s, o] = l.useState(e); + l.useEffect(() => { + n.current.last === e || (n.current.last = e, n.current.pending === null ? o(e) : n.current.pending === e ? (n.current.pending = null, e !== s && (n.current.pending = s, t(s))) : (n.current.pending = null, o(e))); + }, [e, s, t]); + const c = l.useCallback(i => { + o(i), n.current.pending === null && n.current.last !== i && (n.current.pending = i, t(i)); + }, [t]); + return l.useMemo(() => [s, c], [s, c]); +} +a(Wr, "useOptimisticState"); function ce() { let { editorTheme: e = Be, @@ -65582,7 +59507,7 @@ function ce() { } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; let o = arguments.length > 1 ? arguments[1] : undefined; const { - initialHeaders: u, + initialHeaders: c, headerEditor: i, setHeaderEditor: d, shouldPersistHeaders: g @@ -65605,7 +59530,7 @@ function ce() { const C = h.current; if (!C) return; const E = f(C, { - value: u, + value: c, lineNumbers: !0, tabSize: 2, mode: { @@ -65659,18 +59584,18 @@ function ce() { }), () => { x = !1; }; - }, [e, u, s, d]), $e(i, "keyMap", t), pn(i, n, g ? Me : null, "headers", ce), Y(i, ["Cmd-Enter", "Ctrl-Enter"], m == null ? void 0 : m.run), Y(i, ["Shift-Ctrl-P"], y), Y(i, ["Shift-Ctrl-M"], p), h; + }, [e, c, s, d]), $e(i, "keyMap", t), pn(i, n, g ? Me : null, "headers", ce), Y(i, ["Cmd-Enter", "Ctrl-Enter"], m == null ? void 0 : m.run), Y(i, ["Shift-Ctrl-P"], y), Y(i, ["Shift-Ctrl-M"], p), h; } a(ce, "useHeaderEditor"); const Me = "headers", - Wr = Array.from({ + _r = Array.from({ length: 11 }, (e, t) => String.fromCharCode(8192 + t)).concat(["\u2028", "\u2029", " ", " "]), - _r = new RegExp("[" + Wr.join("") + "]", "g"); -function Zr(e) { - return e.replace(_r, " "); + Zr = new RegExp("[" + _r.join("") + "]", "g"); +function $r(e) { + return e.replace(Zr, " "); } -a(Zr, "normalizeWhitespace"); +a($r, "normalizeWhitespace"); function ne() { let { editorTheme: e = Be, @@ -65678,7 +59603,7 @@ function ne() { onClickReference: n, onCopyQuery: s, onEdit: o, - readOnly: u = !1 + readOnly: c = !1 } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; let i = arguments.length > 1 ? arguments[1] : undefined; const { @@ -65774,7 +59699,7 @@ function ne() { autoCloseBrackets: !0, matchBrackets: !0, showCursorWhenSelecting: !0, - readOnly: u ? "nocursor" : !1, + readOnly: c ? "nocursor" : !1, lint: { schema: void 0, validationRules: null, @@ -65839,7 +59764,7 @@ function ne() { }); } }), S.on("keyup", (v, j) => { - zr.test(j.key) && v.execCommand("autocomplete"); + Ur.test(j.key) && v.execCommand("autocomplete"); }); let W = !1; S.on("startCompletion", () => { @@ -65851,39 +59776,39 @@ function ne() { }), S.on("beforeChange", (v, j) => { var R; if (j.origin === "paste") { - const $ = j.text.map(Zr); + const $ = j.text.map($r); (R = j.update) == null || R.call(j, j.from, j.to, $); } }), S.documentAST = null, S.operationName = null, S.operations = null, S.variableToType = null, h(S); }), () => { q = !1; }; - }, [e, m, u, h]), $e(p, "keyMap", t), l.useEffect(() => { + }, [e, m, c, h]), $e(p, "keyMap", t), l.useEffect(() => { if (!p) return; function q(P) { - var _P$operations, _P$operationName, _ref58, _ref59; + var _P$operations, _P$operationName, _ref59, _ref60; var v; const S = kt.getOperationFacts(d, P.getValue()), W = B.getSelectedOperationName((_P$operations = P.operations) !== null && _P$operations !== void 0 ? _P$operations : void 0, (_P$operationName = P.operationName) !== null && _P$operationName !== void 0 ? _P$operationName : void 0, S == null ? void 0 : S.operations); - return P.documentAST = (_ref58 = S == null ? void 0 : S.documentAST) !== null && _ref58 !== void 0 ? _ref58 : null, P.operationName = W !== null && W !== void 0 ? W : null, P.operations = (_ref59 = S == null ? void 0 : S.operations) !== null && _ref59 !== void 0 ? _ref59 : null, f && (f.state.lint.linterOptions.variableToType = S == null ? void 0 : S.variableToType, f.options.lint.variableToType = S == null ? void 0 : S.variableToType, f.options.hintOptions.variableToType = S == null ? void 0 : S.variableToType, (v = I.current) == null || v.signal(f, "change", f)), S ? { + return P.documentAST = (_ref59 = S == null ? void 0 : S.documentAST) !== null && _ref59 !== void 0 ? _ref59 : null, P.operationName = W !== null && W !== void 0 ? W : null, P.operations = (_ref60 = S == null ? void 0 : S.operations) !== null && _ref60 !== void 0 ? _ref60 : null, f && (f.state.lint.linterOptions.variableToType = S == null ? void 0 : S.variableToType, f.options.lint.variableToType = S == null ? void 0 : S.variableToType, f.options.hintOptions.variableToType = S == null ? void 0 : S.variableToType, (v = I.current) == null || v.signal(f, "change", f)), S ? { ...S, operationName: W } : null; } a(q, "getAndUpdateOperationFacts"); const N = ue(100, P => { - var _ref60; + var _ref61; const S = P.getValue(); k == null || k.set(xn, S); const W = P.operationName, v = q(P); - (v == null ? void 0 : v.operationName) !== void 0 && (k == null || k.set(Ur, v.operationName)), o == null || o(S, v == null ? void 0 : v.documentAST), v != null && v.operationName && W !== v.operationName && y(v.operationName), C({ + (v == null ? void 0 : v.operationName) !== void 0 && (k == null || k.set(Kr, v.operationName)), o == null || o(S, v == null ? void 0 : v.documentAST), v != null && v.operationName && W !== v.operationName && y(v.operationName), C({ query: S, - operationName: (_ref60 = v == null ? void 0 : v.operationName) !== null && _ref60 !== void 0 ? _ref60 : null + operationName: (_ref61 = v == null ? void 0 : v.operationName) !== null && _ref61 !== void 0 ? _ref61 : null }); }); return q(p), p.on("change", N), () => p.off("change", N); - }, [o, p, d, y, k, f, C]), $r(p, d !== null && d !== void 0 ? d : null, I), Gr(p, x !== null && x !== void 0 ? x : null, I), Qr(p, g, I), gn(p, n || null, ne); + }, [o, p, d, y, k, f, C]), Gr(p, d !== null && d !== void 0 ? d : null, I), Qr(p, x !== null && x !== void 0 ? x : null, I), zr(p, g, I), gn(p, n || null, ne); const O = E == null ? void 0 : E.run, D = l.useCallback(() => { var P; @@ -65899,23 +59824,23 @@ function ne() { return Y(p, ["Cmd-Enter", "Ctrl-Enter"], D), Y(p, ["Shift-Ctrl-C"], w), Y(p, ["Shift-Ctrl-P", "Shift-Ctrl-F"], A), Y(p, ["Shift-Ctrl-M"], T), F; } a(ne, "useQueryEditor"); -function $r(e, t, n) { +function Gr(e, t, n) { l.useEffect(() => { if (!e) return; const s = e.options.lint.schema !== t; e.state.lint.linterOptions.schema = t, e.options.lint.schema = t, e.options.hintOptions.schema = t, e.options.info.schema = t, e.options.jump.schema = t, s && n.current && n.current.signal(e, "change", e); }, [e, t, n]); } -a($r, "useSynchronizeSchema"); -function Gr(e, t, n) { +a(Gr, "useSynchronizeSchema"); +function Qr(e, t, n) { l.useEffect(() => { if (!e) return; const s = e.options.lint.validationRules !== t; e.state.lint.linterOptions.validationRules = t, e.options.lint.validationRules = t, s && n.current && n.current.signal(e, "change", e); }, [e, t, n]); } -a(Gr, "useSynchronizeValidationRules"); -function Qr(e, t, n) { +a(Qr, "useSynchronizeValidationRules"); +function zr(e, t, n) { const s = l.useMemo(() => [...t.values()], [t]); l.useEffect(() => { if (!e) return; @@ -65923,30 +59848,30 @@ function Qr(e, t, n) { e.state.lint.linterOptions.externalFragments = s, e.options.lint.externalFragments = s, e.options.hintOptions.externalFragments = s, o && n.current && n.current.signal(e, "change", e); }, [e, s, n]); } -a(Qr, "useSynchronizeExternalFragments"); -const zr = /^[a-zA-Z0-9_@(]$/, +a(zr, "useSynchronizeExternalFragments"); +const Ur = /^[a-zA-Z0-9_@(]$/, xn = "query", - Ur = "operationName"; -function Kr(_ref61) { + Kr = "operationName"; +function Jr(_ref62) { let { defaultQuery: e, defaultHeaders: t, headers: n, defaultTabs: s, query: o, - variables: u, + variables: c, storage: i, shouldPersistHeaders: d - } = _ref61; + } = _ref62; const g = i == null ? void 0 : i.get(ve); try { if (!g) throw new Error("Storage for tabs is empty"); const m = JSON.parse(g), p = d ? n : void 0; - if (Jr(m)) { + if (Yr(m)) { const y = De({ query: o, - variables: u, + variables: c, headers: p }); let h = -1; @@ -65965,7 +59890,7 @@ function Kr(_ref61) { hash: y, title: x || xt, query: o, - variables: u, + variables: c, headers: n, operationName: x, response: null @@ -65979,25 +59904,25 @@ function Kr(_ref61) { activeTabIndex: 0, tabs: (s || [{ query: o !== null && o !== void 0 ? o : e, - variables: u, + variables: c, headers: n !== null && n !== void 0 ? n : t }]).map(vn) }; } } -a(Kr, "getDefaultTabState"); -function Jr(e) { - return e && typeof e == "object" && !Array.isArray(e) && Xr(e, "activeTabIndex") && "tabs" in e && Array.isArray(e.tabs) && e.tabs.every(Yr); -} -a(Jr, "isTabsState"); +a(Jr, "getDefaultTabState"); function Yr(e) { + return e && typeof e == "object" && !Array.isArray(e) && es(e, "activeTabIndex") && "tabs" in e && Array.isArray(e.tabs) && e.tabs.every(Xr); +} +a(Yr, "isTabsState"); +function Xr(e) { return e && typeof e == "object" && !Array.isArray(e) && jt(e, "id") && jt(e, "title") && fe(e, "query") && fe(e, "variables") && fe(e, "headers") && fe(e, "operationName") && fe(e, "response"); } -a(Yr, "isTabState"); -function Xr(e, t) { +a(Xr, "isTabState"); +function es(e, t) { return t in e && typeof e[t] == "number"; } -a(Xr, "hasNumberKey"); +a(es, "hasNumberKey"); function jt(e, t) { return t in e && typeof e[t] == "string"; } @@ -66006,22 +59931,22 @@ function fe(e, t) { return t in e && (typeof e[t] == "string" || e[t] === null); } a(fe, "hasStringOrNullKey"); -function es(_ref62) { +function ts(_ref63) { let { queryEditor: e, variableEditor: t, headerEditor: n, responseEditor: s - } = _ref62; + } = _ref63; return l.useCallback(o => { - var _ref63, _ref64, _ref65, _ref66, _ref67; - const u = (_ref63 = e == null ? void 0 : e.getValue()) !== null && _ref63 !== void 0 ? _ref63 : null, - i = (_ref64 = t == null ? void 0 : t.getValue()) !== null && _ref64 !== void 0 ? _ref64 : null, - d = (_ref65 = n == null ? void 0 : n.getValue()) !== null && _ref65 !== void 0 ? _ref65 : null, - g = (_ref66 = e == null ? void 0 : e.operationName) !== null && _ref66 !== void 0 ? _ref66 : null, - m = (_ref67 = s == null ? void 0 : s.getValue()) !== null && _ref67 !== void 0 ? _ref67 : null; + var _ref64, _ref65, _ref66, _ref67, _ref68; + const c = (_ref64 = e == null ? void 0 : e.getValue()) !== null && _ref64 !== void 0 ? _ref64 : null, + i = (_ref65 = t == null ? void 0 : t.getValue()) !== null && _ref65 !== void 0 ? _ref65 : null, + d = (_ref66 = n == null ? void 0 : n.getValue()) !== null && _ref66 !== void 0 ? _ref66 : null, + g = (_ref67 = e == null ? void 0 : e.operationName) !== null && _ref67 !== void 0 ? _ref67 : null, + m = (_ref68 = s == null ? void 0 : s.getValue()) !== null && _ref68 !== void 0 ? _ref68 : null; return yn(o, { - query: u, + query: c, variables: i, headers: d, response: m, @@ -66029,17 +59954,17 @@ function es(_ref62) { }); }, [e, t, n, s]); } -a(es, "useSynchronizeActiveTabValues"); +a(ts, "useSynchronizeActiveTabValues"); function Cn(e) { let t = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !1; return JSON.stringify(e, (n, s) => n === "hash" || n === "response" || !t && n === "headers" ? null : s); } a(Cn, "serializeTabState"); -function ts(_ref68) { +function ns(_ref69) { let { storage: e, shouldPersistHeaders: t - } = _ref68; + } = _ref69; const n = l.useMemo(() => ue(500, s => { e == null || e.set(ve, s); }), [e]); @@ -66047,25 +59972,25 @@ function ts(_ref68) { n(Cn(s, t)); }, [t, n]); } -a(ts, "useStoreTabs"); -function ns(_ref69) { +a(ns, "useStoreTabs"); +function rs(_ref70) { let { queryEditor: e, variableEditor: t, headerEditor: n, responseEditor: s - } = _ref69; - return l.useCallback(_ref70 => { + } = _ref70; + return l.useCallback(_ref71 => { let { query: o, - variables: u, + variables: c, headers: i, response: d - } = _ref70; - e == null || e.setValue(o !== null && o !== void 0 ? o : ""), t == null || t.setValue(u !== null && u !== void 0 ? u : ""), n == null || n.setValue(i !== null && i !== void 0 ? i : ""), s == null || s.setValue(d !== null && d !== void 0 ? d : ""); + } = _ref71; + e == null || e.setValue(o !== null && o !== void 0 ? o : ""), t == null || t.setValue(c !== null && c !== void 0 ? c : ""), n == null || n.setValue(i !== null && i !== void 0 ? i : ""), s == null || s.setValue(d !== null && d !== void 0 ? d : ""); }, [n, e, s, t]); } -a(ns, "useSetEditorValues"); +a(rs, "useSetEditorValues"); function vn() { let { query: e = null, @@ -66117,19 +60042,19 @@ function De(e) { } a(De, "hashFromTabContents"); function gt(e) { - var _ref71; + var _ref72; const n = /^(?!#).*(query|subscription|mutation)\s+([a-zA-Z0-9_]+)/m.exec(e); - return (_ref71 = n == null ? void 0 : n[2]) !== null && _ref71 !== void 0 ? _ref71 : null; + return (_ref72 = n == null ? void 0 : n[2]) !== null && _ref72 !== void 0 ? _ref72 : null; } a(gt, "fuzzyExtractOperationName"); -function rs(e) { +function ss(e) { const t = e == null ? void 0 : e.get(ve); if (t) { const n = JSON.parse(t); e == null || e.set(ve, JSON.stringify(n, (s, o) => s === "headers" ? null : o)); } } -a(rs, "clearHeadersFromTabs"); +a(ss, "clearHeadersFromTabs"); const xt = "", ve = "tabState"; function oe() { @@ -66140,21 +60065,21 @@ function oe() { onEdit: s, readOnly: o = !1 } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; - let u = arguments.length > 1 ? arguments[1] : undefined; + let c = arguments.length > 1 ? arguments[1] : undefined; const { initialVariables: i, variableEditor: d, setVariableEditor: g } = Z({ nonNull: !0, - caller: u || oe + caller: c || oe }), m = we(), p = he({ - caller: u || oe + caller: c || oe }), y = Se({ - caller: u || oe + caller: c || oe }), h = l.useRef(null), x = l.useRef(); @@ -66234,7 +60159,7 @@ const wn = "variables", function En(e) { const t = se(), [n, s] = l.useState(null), - [o, u] = l.useState(null), + [o, c] = l.useState(null), [i, d] = l.useState(null), [g, m] = l.useState(null), [p, y] = l.useState(() => { @@ -66242,30 +60167,30 @@ function En(e) { return e.shouldPersistHeaders !== !1 && v ? (t == null ? void 0 : t.get(ze)) === "true" : !!e.shouldPersistHeaders; }); ke(n, e.headers), ke(o, e.query), ke(i, e.response), ke(g, e.variables); - const h = ts({ + const h = ns({ storage: t, shouldPersistHeaders: p }), [x] = l.useState(() => { - var _ref72, _e$query2, _ref73, _e$variables2, _ref74, _e$headers2, _e$response, _ref75, _ref76; - const v = (_ref72 = (_e$query2 = e.query) !== null && _e$query2 !== void 0 ? _e$query2 : t == null ? void 0 : t.get(xn)) !== null && _ref72 !== void 0 ? _ref72 : null, - j = (_ref73 = (_e$variables2 = e.variables) !== null && _e$variables2 !== void 0 ? _e$variables2 : t == null ? void 0 : t.get(wn)) !== null && _ref73 !== void 0 ? _ref73 : null, - R = (_ref74 = (_e$headers2 = e.headers) !== null && _e$headers2 !== void 0 ? _e$headers2 : t == null ? void 0 : t.get(Me)) !== null && _ref74 !== void 0 ? _ref74 : null, + var _ref73, _e$query2, _ref74, _e$variables2, _ref75, _e$headers2, _e$response, _ref76, _ref77; + const v = (_ref73 = (_e$query2 = e.query) !== null && _e$query2 !== void 0 ? _e$query2 : t == null ? void 0 : t.get(xn)) !== null && _ref73 !== void 0 ? _ref73 : null, + j = (_ref74 = (_e$variables2 = e.variables) !== null && _e$variables2 !== void 0 ? _e$variables2 : t == null ? void 0 : t.get(wn)) !== null && _ref74 !== void 0 ? _ref74 : null, + R = (_ref75 = (_e$headers2 = e.headers) !== null && _e$headers2 !== void 0 ? _e$headers2 : t == null ? void 0 : t.get(Me)) !== null && _ref75 !== void 0 ? _ref75 : null, $ = (_e$response = e.response) !== null && _e$response !== void 0 ? _e$response : "", - z = Kr({ + z = Jr({ query: v, variables: j, headers: R, defaultTabs: e.defaultTabs, - defaultQuery: e.defaultQuery || ss, + defaultQuery: e.defaultQuery || os, defaultHeaders: e.defaultHeaders, storage: t, shouldPersistHeaders: p }); return h(z), { - query: (_ref75 = v !== null && v !== void 0 ? v : z.activeTabIndex === 0 ? z.tabs[0].query : null) !== null && _ref75 !== void 0 ? _ref75 : "", + query: (_ref76 = v !== null && v !== void 0 ? v : z.activeTabIndex === 0 ? z.tabs[0].query : null) !== null && _ref76 !== void 0 ? _ref76 : "", variables: j !== null && j !== void 0 ? j : "", - headers: (_ref76 = R !== null && R !== void 0 ? R : e.defaultHeaders) !== null && _ref76 !== void 0 ? _ref76 : "", + headers: (_ref77 = R !== null && R !== void 0 ? R : e.defaultHeaders) !== null && _ref77 !== void 0 ? _ref77 : "", response: $, tabState: z }; @@ -66273,11 +60198,11 @@ function En(e) { [f, C] = l.useState(x.tabState), E = l.useCallback(v => { if (v) { - var _ref77; - t == null || t.set(Me, (_ref77 = n == null ? void 0 : n.getValue()) !== null && _ref77 !== void 0 ? _ref77 : ""); + var _ref78; + t == null || t.set(Me, (_ref78 = n == null ? void 0 : n.getValue()) !== null && _ref78 !== void 0 ? _ref78 : ""); const j = Cn(f, !0); t == null || t.set(ve, j); - } else t == null || t.set(Me, ""), rs(t); + } else t == null || t.set(Me, ""), ss(t); y(v), t == null || t.set(ze, v.toString()); }, [t, f, n]), k = l.useRef(); @@ -66285,13 +60210,13 @@ function En(e) { const v = !!e.shouldPersistHeaders; (k == null ? void 0 : k.current) !== v && (E(v), k.current = v); }, [e.shouldPersistHeaders, E]); - const L = es({ + const L = ts({ queryEditor: o, variableEditor: g, headerEditor: n, responseEditor: i }), - b = ns({ + b = rs({ queryEditor: o, variableEditor: g, headerEditor: n, @@ -66378,7 +60303,7 @@ function En(e) { responseEditor: i, variableEditor: g, setHeaderEditor: s, - setQueryEditor: u, + setQueryEditor: c, setResponseEditor: d, setVariableEditor: m, setOperationName: N, @@ -66399,7 +60324,7 @@ function En(e) { a(En, "EditorContextProvider"); const Z = ie(Ct), ze = "shouldPersistHeaders", - ss = `# Welcome to GraphiQL + os = `# Welcome to GraphiQL # # GraphiQL is an in-browser tool for writing, validating, and # testing GraphQL queries. @@ -66431,11 +60356,11 @@ const Z = ie(Ct), # `; -function Xe(_ref78) { +function Xe(_ref79) { let { isHidden: e, ...t - } = _ref78; + } = _ref79; const { headerEditor: n } = Z({ @@ -66458,10 +60383,10 @@ function Ae(e) { height: null }), [s, o] = l.useState(null), - u = l.useRef(null), + c = l.useRef(null), i = (g = Sn(e.token)) == null ? void 0 : g.href; l.useEffect(() => { - if (u.current) { + if (c.current) { if (!i) { n({ width: null, @@ -66484,14 +60409,14 @@ function Ae(e) { return r.jsxs("div", { children: [r.jsx("img", { onLoad: () => { - var _ref79, _ref80; + var _ref80, _ref81; var m, p; n({ - width: (_ref79 = (m = u.current) == null ? void 0 : m.naturalWidth) !== null && _ref79 !== void 0 ? _ref79 : null, - height: (_ref80 = (p = u.current) == null ? void 0 : p.naturalHeight) !== null && _ref80 !== void 0 ? _ref80 : null + width: (_ref80 = (m = c.current) == null ? void 0 : m.naturalWidth) !== null && _ref80 !== void 0 ? _ref80 : null, + height: (_ref81 = (p = c.current) == null ? void 0 : p.naturalHeight) !== null && _ref81 !== void 0 ? _ref81 : null }); }, - ref: u, + ref: c, src: i }), d] }); @@ -66499,7 +60424,7 @@ function Ae(e) { a(Ae, "ImagePreview"); Ae.shouldRender = a(function (t) { const n = Sn(t); - return n ? os(n) : !1; + return n ? ls(n) : !1; }, "shouldRender"); function Sn(e) { if (e.type !== "string") return; @@ -66514,10 +60439,10 @@ function Sn(e) { } } a(Sn, "tokenToURL"); -function os(e) { +function ls(e) { return /(bmp|gif|jpeg|jpg|png|svg)$/.test(e.pathname); } -a(os, "isImageURL"); +a(ls, "isImageURL"); function Ln(e) { const t = ne(e, Ln); return r.jsx("div", { @@ -66535,7 +60460,7 @@ function Oe() { let s = arguments.length > 1 ? arguments[1] : undefined; const { fetchError: o, - validationErrors: u + validationErrors: c } = X({ nonNull: !0, caller: s || Oe @@ -66587,8 +60512,8 @@ function Oe() { y = !1; }; }, [t, i, g]), $e(d, "keyMap", n), l.useEffect(() => { - o && (d == null || d.setValue(o)), u.length > 0 && (d == null || d.setValue(B.formatError(u))); - }, [d, o, u]), m; + o && (d == null || d.setValue(o)), c.length > 0 && (d == null || d.setValue(B.formatError(c))); + }, [d, o, c]), m; } a(Oe, "useResponseEditor"); function jn(e) { @@ -66602,11 +60527,11 @@ function jn(e) { }); } a(jn, "ResponseEditor"); -function et(_ref81) { +function et(_ref82) { let { isHidden: e, ...t - } = _ref81; + } = _ref82; const { variableEditor: n } = Z({ @@ -66622,14 +60547,14 @@ function et(_ref81) { }); } a(et, "VariableEditor"); -function ls(_ref82) { +function is(_ref83) { let { children: e, dangerouslyAssumeSchemaIsValid: t, defaultQuery: n, defaultHeaders: s, defaultTabs: o, - externalFragments: u, + externalFragments: c, fetcher: i, getDefaultFieldNames: d, headers: g, @@ -66651,7 +60576,7 @@ function ls(_ref82) { validationRules: I, variables: H, visiblePlugin: O - } = _ref82; + } = _ref83; return r.jsx(Tt, { storage: F, children: r.jsx(on, { @@ -66660,7 +60585,7 @@ function ls(_ref82) { defaultQuery: n, defaultHeaders: s, defaultTabs: o, - externalFragments: u, + externalFragments: c, headers: g, onEditOperationName: h, onTabChange: f, @@ -66695,8 +60620,8 @@ function ls(_ref82) { }) }); } -a(ls, "GraphiQLProvider"); -function is() { +a(is, "GraphiQLProvider"); +function as() { const e = se(), [t, n] = l.useState(() => { if (!e) return null; @@ -66721,18 +60646,18 @@ function is() { setTheme: s }), [t, s]); } -a(is, "useTheme"); +a(as, "useTheme"); const Ue = "theme"; -function as(_ref83) { +function cs(_ref84) { let { - defaultSizeRelation: e = cs, + defaultSizeRelation: e = us, direction: t, initiallyHidden: n, onHiddenElementChange: s, sizeThresholdFirst: o = 100, - sizeThresholdSecond: u = 100, + sizeThresholdSecond: c = 100, storageKey: i - } = _ref83; + } = _ref84; const d = se(), g = l.useMemo(() => ue(500, L => { i && (d == null || d.set(i, L)); @@ -66784,7 +60709,7 @@ function as(_ref83) { if (S.buttons === 0) return P(); const W = S[T] - w.getBoundingClientRect()[A] - q, v = w.getBoundingClientRect()[F] - S[T] + q - L[I]; - if (W < o) y("first"), g(Ne);else if (v < u) y("second"), g(Te);else { + if (W < o) y("first"), g(Ne);else if (v < c) y("second"), g(Te);else { y(null); const j = `${W / v}`; b.style.flex = j, g(j); @@ -66803,7 +60728,7 @@ function as(_ref83) { return a(O, "reset"), L.addEventListener("dblclick", O), () => { L.removeEventListener("mousedown", H), L.removeEventListener("dblclick", O); }; - }, [t, y, o, u, g]), l.useMemo(() => ({ + }, [t, y, o, c, g]), l.useMemo(() => ({ dragBarRef: x, hiddenElement: m, firstRef: h, @@ -66811,22 +60736,22 @@ function as(_ref83) { secondRef: f }), [m, p]); } -a(as, "useDragResize"); -const cs = 1, +a(cs, "useDragResize"); +const us = 1, Ne = "hide-first", Te = "hide-second"; -const kn = l.forwardRef((_ref84, s) => { +const kn = l.forwardRef((_ref85, s) => { let { label: e, onClick: t, ...n - } = _ref84; - const [o, u] = l.useState(null), + } = _ref85; + const [o, c] = l.useState(null), i = l.useCallback(d => { try { - t == null || t(d), u(null); + t == null || t(d), c(null); } catch (g) { - u(g instanceof Error ? g : new Error(`Toolbar button click failed: ${g}`)); + c(g instanceof Error ? g : new Error(`Toolbar button click failed: ${g}`)); } }, [t]); return r.jsx(J, { @@ -66855,7 +60780,7 @@ function tt() { isFetching: n, isSubscribed: s, operationName: o, - run: u, + run: c, stop: i } = we({ nonNull: !0, @@ -66884,7 +60809,7 @@ function tt() { onSelect: () => { var E; const C = (E = h.name) == null ? void 0 : E.value; - e && C && C !== e.operationName && t(C), u(); + e && C && C !== e.operationName && t(C), c(); }, children: f }, `${f}-${x}`); @@ -66895,19 +60820,19 @@ function tt() { children: r.jsx("button", { ...y, onClick: () => { - m ? i() : u(); + m ? i() : c(); } }) }); } a(tt, "ExecuteButton"); -const us = a(_ref85 => { +const ds = a(_ref86 => { let { button: e, children: t, label: n, ...s - } = _ref85; + } = _ref86; return r.jsxs(ee, { ...s, children: [r.jsx(J, { @@ -66922,7 +60847,7 @@ const us = a(_ref85 => { })] }); }, "ToolbarMenuRoot"), - ds = ye(us, { + hs = ye(ds, { Item: ee.Item }); exports.Argument = Ce; @@ -66960,7 +60885,7 @@ exports.ExplorerSection = G; exports.FieldDocumentation = un; exports.FieldIcon = Ot; exports.FieldLink = hn; -exports.GraphiQLProvider = ls; +exports.GraphiQLProvider = is; exports.HISTORY_PLUGIN = Ye; exports.HeaderEditor = Xe; exports.History = ln; @@ -66997,7 +60922,7 @@ exports.StorageContextProvider = Tt; exports.Tab = Lr; exports.Tabs = sn; exports.ToolbarButton = kn; -exports.ToolbarMenu = ds; +exports.ToolbarMenu = hs; exports.Tooltip = J; exports.TooltipRoot = en; exports.TrashIcon = Ut; @@ -67008,7 +60933,7 @@ exports.UnStyledButton = Q; exports.VariableEditor = et; exports.useAutoCompleteLeafs = He; exports.useCopyQuery = pt; -exports.useDragResize = as; +exports.useDragResize = cs; exports.useEditorContext = Z; exports.useEditorState = Ge; exports.useExecutionContext = we; @@ -67018,13 +60943,14 @@ exports.useHeadersEditorState = Br; exports.useHistoryContext = be; exports.useMergeQuery = he; exports.useOperationsEditorState = Or; +exports.useOptimisticState = Wr; exports.usePluginContext = Ze; exports.usePrettifyEditors = Se; exports.useQueryEditor = ne; exports.useResponseEditor = Oe; exports.useSchemaContext = X; exports.useStorageContext = se; -exports.useTheme = is; +exports.useTheme = as; exports.useVariableEditor = oe; exports.useVariablesEditorState = Fr; @@ -67194,7 +61120,7 @@ D.CodeMirror.registerHelper("info", "graphql", (a, e) => { } }); function E(a, e, c) { - N(a, e, c), f(a, e, c, e.type); + N(a, e, c), p(a, e, c, e.type); } l(E, "renderField"); function N(a, e, c) { @@ -67212,7 +61138,7 @@ l(h, "renderDirective"); function T(a, e, c) { var n; const r = ((n = e.argDef) === null || n === void 0 ? void 0 : n.name) || ""; - t(a, r, "arg-name", c, m.getArgumentReference(e)), f(a, e, c, e.inputType); + t(a, r, "arg-name", c, m.getArgumentReference(e)), p(a, e, c, e.inputType); } l(T, "renderArg"); function g(a, e, c) { @@ -67221,11 +61147,11 @@ function g(a, e, c) { u(a, e, c, e.inputType), t(a, "."), t(a, r, "enum-value", c, m.getEnumValueReference(e)); } l(g, "renderEnumValue"); -function f(a, e, c, n) { +function p(a, e, c, n) { const r = document.createElement("span"); r.className = "type-name-pill", n instanceof s.GraphQLNonNull ? (u(r, e, c, n.ofType), t(r, "!")) : n instanceof s.GraphQLList ? (t(r, "["), u(r, e, c, n.ofType), t(r, "]")) : t(r, (n == null ? void 0 : n.name) || "", "type-name", c, m.getTypeReference(e, n)), a.append(r); } -l(f, "renderTypeAnnotation"); +l(p, "renderTypeAnnotation"); function u(a, e, c, n) { n instanceof s.GraphQLNonNull ? (u(a, e, c, n.ofType), t(a, "!")) : n instanceof s.GraphQLList ? (t(a, "["), u(a, e, c, n.ofType), t(a, "]")) : t(a, (n == null ? void 0 : n.name) || "", "type-name", c, m.getTypeReference(e, n)); } @@ -67264,8 +61190,8 @@ function t(a, e) { onClick: i } = n; let d; - i ? (d = document.createElement("a"), d.href = "javascript:void 0", d.addEventListener("click", p => { - i(r, p); + i ? (d = document.createElement("a"), d.href = "javascript:void 0", d.addEventListener("click", f => { + f.preventDefault(), i(r, f); })) : d = document.createElement("span"), d.className = c, d.append(document.createTextNode(e)), a.append(d); } else a.append(document.createTextNode(e)); } @@ -68087,7 +62013,7 @@ var c = (u, p) => m(u, "name", { configurable: !0 }); const f = __webpack_require__(/*! ./codemirror.cjs2.js */ "../../graphiql-react/dist/codemirror.cjs2.js"), - g = __webpack_require__(/*! ./dialog.cjs2.js */ "../../graphiql-react/dist/dialog.cjs2.js"); + g = __webpack_require__(/*! ./dialog.cjs.js */ "../../graphiql-react/dist/dialog.cjs.js"); function h(u, p) { for (var o = 0; o < p.length; o++) { const s = p[o]; @@ -68111,36 +62037,36 @@ var b = { }; (function (u, p) { (function (o) { - o(f.requireCodemirror(), g.requireDialog()); + o(f.requireCodemirror(), g.dialogExports); })(function (o) { o.defineOption("search", { bottom: !1 }); - function s(e, t, n, r, l) { - e.openDialog ? e.openDialog(t, l, { - value: r, + function s(e, r, n, t, l) { + e.openDialog ? e.openDialog(r, l, { + value: t, selectValueOnOpen: !0, bottom: e.options.search.bottom - }) : l(prompt(n, r)); + }) : l(prompt(n, t)); } c(s, "dialog"); function i(e) { return e.phrase("Jump to line:") + ' ' + e.phrase("(Use line:column or scroll% syntax)") + ""; } c(i, "getJumpDialog"); - function a(e, t) { - var n = Number(t); - return /^[-+]/.test(t) ? e.getCursor().line + n : n - 1; + function a(e, r) { + var n = Number(r); + return /^[-+]/.test(r) ? e.getCursor().line + n : n - 1; } c(a, "interpretLine"), o.commands.jumpToLine = function (e) { - var t = e.getCursor(); - s(e, i(e), e.phrase("Jump to line:"), t.line + 1 + ":" + t.ch, function (n) { + var r = e.getCursor(); + s(e, i(e), e.phrase("Jump to line:"), r.line + 1 + ":" + r.ch, function (n) { if (n) { - var r; - if (r = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(n)) e.setCursor(a(e, r[1]), Number(r[2]));else if (r = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(n)) { - var l = Math.round(e.lineCount() * Number(r[1]) / 100); - /^[-+]/.test(r[1]) && (l = t.line + l + 1), e.setCursor(l - 1, t.ch); - } else (r = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(n)) && e.setCursor(a(e, r[1]), t.ch); + var t; + if (t = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(n)) e.setCursor(a(e, t[1]), Number(t[2]));else if (t = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(n)) { + var l = Math.round(e.lineCount() * Number(t[1]) / 100); + /^[-+]/.test(t[1]) && (l = r.line + l + 1), e.setCursor(l - 1, r.ch); + } else (t = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(n)) && e.setCursor(a(e, t[1]), r.ch); } }); }, o.keyMap.default["Alt-G"] = "jumpToLine"; @@ -69288,7 +63214,7 @@ var a = (S, O) => K(S, "name", { }); const Q = __webpack_require__(/*! ./codemirror.cjs2.js */ "../../graphiql-react/dist/codemirror.cjs2.js"), L = __webpack_require__(/*! ./searchcursor.cjs2.js */ "../../graphiql-react/dist/searchcursor.cjs2.js"), - z = __webpack_require__(/*! ./dialog.cjs2.js */ "../../graphiql-react/dist/dialog.cjs2.js"); + z = __webpack_require__(/*! ./dialog.cjs.js */ "../../graphiql-react/dist/dialog.cjs.js"); function U(S, O) { for (var i = 0; i < O.length; i++) { const y = O[i]; @@ -69312,7 +63238,7 @@ var B = { }; (function (S, O) { (function (i) { - i(Q.requireCodemirror(), L.requireSearchcursor(), z.requireDialog()); + i(Q.requireCodemirror(), L.requireSearchcursor(), z.dialogExports); })(function (i) { i.defineOption("search", { bottom: !1 @@ -69360,14 +63286,14 @@ var B = { }); } a(j, "persistentDialog"); - function R(e, n, t, o, r) { + function D(e, n, t, o, r) { e.openDialog ? e.openDialog(n, r, { value: o, selectValueOnOpen: !0, bottom: e.options.search.bottom }) : r(prompt(t, o)); } - a(R, "dialog"); + a(D, "dialog"); function k(e, n, t, o) { e.openConfirm ? e.openConfirm(n, o) : confirm(t) && o[0](); } @@ -69387,36 +63313,36 @@ var B = { return (typeof e == "string" ? e == "" : e.test("")) && (e = /x^/), e; } a(T, "parseQuery"); - function D(e, n, t) { + function P(e, n, t) { n.queryText = t, n.query = T(t), e.removeOverlay(n.overlay, m(n.query)), n.overlay = y(n.query, m(n.query)), e.addOverlay(n.overlay), e.showMatchesOnScrollbar && (n.annotate && (n.annotate.clear(), n.annotate = null), n.annotate = e.showMatchesOnScrollbar(n.query, m(n.query))); } - a(D, "startSearch"); + a(P, "startSearch"); function b(e, n, t, o) { var r = h(e); - if (r.query) return P(e, n); + if (r.query) return R(e, n); var s = e.getSelection() || r.lastQuery; if (s instanceof RegExp && s.source == "x^" && (s = null), t && e.openDialog) { var c = null, u = a(function (f, x) { - i.e_stop(x), f && (f != r.queryText && (D(e, r, f), r.posFrom = r.posTo = e.getCursor()), c && (c.style.opacity = 1), P(e, x.shiftKey, function (d, g) { + i.e_stop(x), f && (f != r.queryText && (P(e, r, f), r.posFrom = r.posTo = e.getCursor()), c && (c.style.opacity = 1), R(e, x.shiftKey, function (d, g) { var p; g.line < 3 && document.querySelector && (p = e.display.wrapper.querySelector(".CodeMirror-dialog")) && p.getBoundingClientRect().bottom - 4 > e.cursorCoords(g, "window").top && ((c = p).style.opacity = .4); })); }, "searchNext"); - j(e, _(e), s, u, function (f, x) { + j(e, E(e), s, u, function (f, x) { var d = i.keyName(f), g = e.getOption("extraKeys"), p = g && g[d] || i.keyMap[e.getOption("keyMap")][d]; - p == "findNext" || p == "findPrev" || p == "findPersistentNext" || p == "findPersistentPrev" ? (i.e_stop(f), D(e, h(e), x), e.execCommand(p)) : (p == "find" || p == "findPersistent") && (i.e_stop(f), u(x, f)); - }), o && s && (D(e, r, s), P(e, n)); - } else R(e, _(e), "Search for:", s, function (f) { + p == "findNext" || p == "findPrev" || p == "findPersistentNext" || p == "findPersistentPrev" ? (i.e_stop(f), P(e, h(e), x), e.execCommand(p)) : (p == "find" || p == "findPersistent") && (i.e_stop(f), u(x, f)); + }), o && s && (P(e, r, s), R(e, n)); + } else D(e, E(e), "Search for:", s, function (f) { f && !r.query && e.operation(function () { - D(e, r, f), r.posFrom = r.posTo = e.getCursor(), P(e, n); + P(e, r, f), r.posFrom = r.posTo = e.getCursor(), R(e, n); }); }); } a(b, "doSearch"); - function P(e, n, t) { + function R(e, n, t) { e.operation(function () { var o = h(e), r = N(e, o.query, n ? o.posFrom : o.posTo); @@ -69426,7 +63352,7 @@ var B = { }, 20), o.posFrom = r.from(), o.posTo = r.to(), t && t(r.from(), r.to())); }); } - a(P, "findNext"); + a(R, "findNext"); function w(e) { e.operation(function () { var n = h(e); @@ -69444,7 +63370,7 @@ var B = { return t; } a(l, "el"); - function _(e) { + function E(e) { return l("", null, l("span", { className: "CodeMirror-search-label" }, e.phrase("Search:")), " ", l("input", { @@ -69456,7 +63382,7 @@ var B = { className: "CodeMirror-search-hint" }, e.phrase("(Use /re/ syntax for regexp search)"))); } - a(_, "getQueryDialog"); + a(E, "getQueryDialog"); function A(e) { return l("", null, " ", l("input", { type: "text", @@ -69484,7 +63410,7 @@ var B = { }, e.phrase("Replace?")), " ", l("button", {}, e.phrase("Yes")), " ", l("button", {}, e.phrase("No")), " ", l("button", {}, e.phrase("All")), " ", l("button", {}, e.phrase("Stop"))); } a(V, "getDoReplaceConfirm"); - function E(e, n, t) { + function _(e, n, t) { e.operation(function () { for (var o = N(e, n); o.findNext();) if (typeof n != "string") { var r = e.getRange(o.from(), o.to()).match(n); @@ -69494,7 +63420,7 @@ var B = { } else o.replace(t); }); } - a(E, "replaceAll"); + a(_, "replaceAll"); function F(e, n) { if (!e.getOption("readOnly")) { var t = e.getSelection() || h(e).lastQuery, @@ -69502,9 +63428,9 @@ var B = { r = l("", null, l("span", { className: "CodeMirror-search-label" }, o), A(e)); - R(e, r, o, t, function (s) { - s && (s = T(s), R(e, I(e), e.phrase("Replace with:"), "", function (c) { - if (c = C(c), n) E(e, s, c);else { + D(e, r, o, t, function (s) { + s && (s = T(s), D(e, I(e), e.phrase("Replace with:"), "", function (c) { + if (c = C(c), n) _(e, s, c);else { w(e); var u = N(e, s, e.getCursor("from")), f = a(function () { @@ -69516,7 +63442,7 @@ var B = { }), k(e, V(e), e.phrase("Replace?"), [function () { x(g); }, f, function () { - E(e, s, c); + _(e, s, c); }])); }, "advance"), x = a(function (d) { @@ -72218,6 +66144,6759 @@ exports.QueryStore = QueryStore; /***/ }), +/***/ "../node_modules/linkify-it/build/index.cjs.js": +/*!*****************************************************!*\ + !*** ../node_modules/linkify-it/build/index.cjs.js ***! + \*****************************************************/ +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + + + +var uc_micro = __webpack_require__(/*! uc.micro */ "../node_modules/uc.micro/build/index.cjs.js"); +function reFactory(opts) { + const re = {}; + opts = opts || {}; + re.src_Any = uc_micro.Any.source; + re.src_Cc = uc_micro.Cc.source; + re.src_Z = uc_micro.Z.source; + re.src_P = uc_micro.P.source; + + // \p{\Z\P\Cc\CF} (white spaces + control + format + punctuation) + re.src_ZPCc = [re.src_Z, re.src_P, re.src_Cc].join('|'); + + // \p{\Z\Cc} (white spaces + control) + re.src_ZCc = [re.src_Z, re.src_Cc].join('|'); + + // Experimental. List of chars, completely prohibited in links + // because can separate it from other part of text + const text_separators = '[><\uff5c]'; + + // All possible word characters (everything without punctuation, spaces & controls) + // Defined via punctuation & spaces to save space + // Should be something like \p{\L\N\S\M} (\w but without `_`) + re.src_pseudo_letter = '(?:(?!' + text_separators + '|' + re.src_ZPCc + ')' + re.src_Any + ')'; + // The same as abothe but without [0-9] + // var src_pseudo_letter_non_d = '(?:(?![0-9]|' + src_ZPCc + ')' + src_Any + ')'; + + re.src_ip4 = '(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'; + + // Prohibit any of "@/[]()" in user/pass to avoid wrong domain fetch. + re.src_auth = '(?:(?:(?!' + re.src_ZCc + '|[@/\\[\\]()]).)+@)?'; + re.src_port = '(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?'; + re.src_host_terminator = '(?=$|' + text_separators + '|' + re.src_ZPCc + ')' + '(?!' + (opts['---'] ? '-(?!--)|' : '-|') + '_|:\\d|\\.-|\\.(?!$|' + re.src_ZPCc + '))'; + re.src_path = '(?:' + '[/?#]' + '(?:' + '(?!' + re.src_ZCc + '|' + text_separators + '|[()[\\]{}.,"\'?!\\-;]).|' + '\\[(?:(?!' + re.src_ZCc + '|\\]).)*\\]|' + '\\((?:(?!' + re.src_ZCc + '|[)]).)*\\)|' + '\\{(?:(?!' + re.src_ZCc + '|[}]).)*\\}|' + '\\"(?:(?!' + re.src_ZCc + '|["]).)+\\"|' + "\\'(?:(?!" + re.src_ZCc + "|[']).)+\\'|" + + // allow `I'm_king` if no pair found + "\\'(?=" + re.src_pseudo_letter + '|[-])|' + + // google has many dots in "google search" links (#66, #81). + // github has ... in commit range links, + // Restrict to + // - english + // - percent-encoded + // - parts of file path + // - params separator + // until more examples found. + '\\.{2,}[a-zA-Z0-9%/&]|' + '\\.(?!' + re.src_ZCc + '|[.]|$)|' + (opts['---'] ? '\\-(?!--(?:[^-]|$))(?:-*)|' // `---` => long dash, terminate + : '\\-+|') + + // allow `,,,` in paths + ',(?!' + re.src_ZCc + '|$)|' + + // allow `;` if not followed by space-like char + ';(?!' + re.src_ZCc + '|$)|' + + // allow `!!!` in paths, but not at the end + '\\!+(?!' + re.src_ZCc + '|[!]|$)|' + '\\?(?!' + re.src_ZCc + '|[?]|$)' + ')+' + '|\\/' + ')?'; + + // Allow anything in markdown spec, forbid quote (") at the first position + // because emails enclosed in quotes are far more common + re.src_email_name = '[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*'; + re.src_xn = 'xn--[a-z0-9\\-]{1,59}'; + + // More to read about domain names + // http://serverfault.com/questions/638260/ + + re.src_domain_root = + // Allow letters & digits (http://test1) + '(?:' + re.src_xn + '|' + re.src_pseudo_letter + '{1,63}' + ')'; + re.src_domain = '(?:' + re.src_xn + '|' + '(?:' + re.src_pseudo_letter + ')' + '|' + '(?:' + re.src_pseudo_letter + '(?:-|' + re.src_pseudo_letter + '){0,61}' + re.src_pseudo_letter + ')' + ')'; + re.src_host = '(?:' + + // Don't need IP check, because digits are already allowed in normal domain names + // src_ip4 + + // '|' + + '(?:(?:(?:' + re.src_domain + ')\\.)*' + re.src_domain /* _root */ + ')' + ')'; + re.tpl_host_fuzzy = '(?:' + re.src_ip4 + '|' + '(?:(?:(?:' + re.src_domain + ')\\.)+(?:%TLDS%))' + ')'; + re.tpl_host_no_ip_fuzzy = '(?:(?:(?:' + re.src_domain + ')\\.)+(?:%TLDS%))'; + re.src_host_strict = re.src_host + re.src_host_terminator; + re.tpl_host_fuzzy_strict = re.tpl_host_fuzzy + re.src_host_terminator; + re.src_host_port_strict = re.src_host + re.src_port + re.src_host_terminator; + re.tpl_host_port_fuzzy_strict = re.tpl_host_fuzzy + re.src_port + re.src_host_terminator; + re.tpl_host_port_no_ip_fuzzy_strict = re.tpl_host_no_ip_fuzzy + re.src_port + re.src_host_terminator; + + // + // Main rules + // + + // Rude test fuzzy links by host, for quick deny + re.tpl_host_fuzzy_test = 'localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:' + re.src_ZPCc + '|>|$))'; + re.tpl_email_fuzzy = '(^|' + text_separators + '|"|\\(|' + re.src_ZCc + ')' + '(' + re.src_email_name + '@' + re.tpl_host_fuzzy_strict + ')'; + re.tpl_link_fuzzy = + // Fuzzy link can't be prepended with .:/\- and non punctuation. + // but can start with > (markdown blockquote) + '(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|' + re.src_ZPCc + '))' + '((?![$+<=>^`|\uff5c])' + re.tpl_host_port_fuzzy_strict + re.src_path + ')'; + re.tpl_link_no_ip_fuzzy = + // Fuzzy link can't be prepended with .:/\- and non punctuation. + // but can start with > (markdown blockquote) + '(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|' + re.src_ZPCc + '))' + '((?![$+<=>^`|\uff5c])' + re.tpl_host_port_no_ip_fuzzy_strict + re.src_path + ')'; + return re; +} + +// +// Helpers +// + +// Merge objects +// +function assign(obj /* from1, from2, from3, ... */) { + const sources = Array.prototype.slice.call(arguments, 1); + sources.forEach(function (source) { + if (!source) { + return; + } + Object.keys(source).forEach(function (key) { + obj[key] = source[key]; + }); + }); + return obj; +} +function _class(obj) { + return Object.prototype.toString.call(obj); +} +function isString(obj) { + return _class(obj) === '[object String]'; +} +function isObject(obj) { + return _class(obj) === '[object Object]'; +} +function isRegExp(obj) { + return _class(obj) === '[object RegExp]'; +} +function isFunction(obj) { + return _class(obj) === '[object Function]'; +} +function escapeRE(str) { + return str.replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&'); +} + +// + +const defaultOptions = { + fuzzyLink: true, + fuzzyEmail: true, + fuzzyIP: false +}; +function isOptionsObj(obj) { + return Object.keys(obj || {}).reduce(function (acc, k) { + /* eslint-disable-next-line no-prototype-builtins */ + return acc || defaultOptions.hasOwnProperty(k); + }, false); +} +const defaultSchemas = { + 'http:': { + validate: function (text, pos, self) { + const tail = text.slice(pos); + if (!self.re.http) { + // compile lazily, because "host"-containing variables can change on tlds update. + self.re.http = new RegExp('^\\/\\/' + self.re.src_auth + self.re.src_host_port_strict + self.re.src_path, 'i'); + } + if (self.re.http.test(tail)) { + return tail.match(self.re.http)[0].length; + } + return 0; + } + }, + 'https:': 'http:', + 'ftp:': 'http:', + '//': { + validate: function (text, pos, self) { + const tail = text.slice(pos); + if (!self.re.no_http) { + // compile lazily, because "host"-containing variables can change on tlds update. + self.re.no_http = new RegExp('^' + self.re.src_auth + + // Don't allow single-level domains, because of false positives like '//test' + // with code comments + '(?:localhost|(?:(?:' + self.re.src_domain + ')\\.)+' + self.re.src_domain_root + ')' + self.re.src_port + self.re.src_host_terminator + self.re.src_path, 'i'); + } + if (self.re.no_http.test(tail)) { + // should not be `://` & `///`, that protects from errors in protocol name + if (pos >= 3 && text[pos - 3] === ':') { + return 0; + } + if (pos >= 3 && text[pos - 3] === '/') { + return 0; + } + return tail.match(self.re.no_http)[0].length; + } + return 0; + } + }, + 'mailto:': { + validate: function (text, pos, self) { + const tail = text.slice(pos); + if (!self.re.mailto) { + self.re.mailto = new RegExp('^' + self.re.src_email_name + '@' + self.re.src_host_strict, 'i'); + } + if (self.re.mailto.test(tail)) { + return tail.match(self.re.mailto)[0].length; + } + return 0; + } + } +}; + +// RE pattern for 2-character tlds (autogenerated by ./support/tlds_2char_gen.js) +/* eslint-disable-next-line max-len */ +const tlds_2ch_src_re = 'a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]'; + +// DON'T try to make PRs with changes. Extend TLDs with LinkifyIt.tlds() instead +const tlds_default = 'biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф'.split('|'); +function resetScanCache(self) { + self.__index__ = -1; + self.__text_cache__ = ''; +} +function createValidator(re) { + return function (text, pos) { + const tail = text.slice(pos); + if (re.test(tail)) { + return tail.match(re)[0].length; + } + return 0; + }; +} +function createNormalizer() { + return function (match, self) { + self.normalize(match); + }; +} + +// Schemas compiler. Build regexps. +// +function compile(self) { + // Load & clone RE patterns. + const re = self.re = reFactory(self.__opts__); + + // Define dynamic patterns + const tlds = self.__tlds__.slice(); + self.onCompile(); + if (!self.__tlds_replaced__) { + tlds.push(tlds_2ch_src_re); + } + tlds.push(re.src_xn); + re.src_tlds = tlds.join('|'); + function untpl(tpl) { + return tpl.replace('%TLDS%', re.src_tlds); + } + re.email_fuzzy = RegExp(untpl(re.tpl_email_fuzzy), 'i'); + re.link_fuzzy = RegExp(untpl(re.tpl_link_fuzzy), 'i'); + re.link_no_ip_fuzzy = RegExp(untpl(re.tpl_link_no_ip_fuzzy), 'i'); + re.host_fuzzy_test = RegExp(untpl(re.tpl_host_fuzzy_test), 'i'); + + // + // Compile each schema + // + + const aliases = []; + self.__compiled__ = {}; // Reset compiled data + + function schemaError(name, val) { + throw new Error('(LinkifyIt) Invalid schema "' + name + '": ' + val); + } + Object.keys(self.__schemas__).forEach(function (name) { + const val = self.__schemas__[name]; + + // skip disabled methods + if (val === null) { + return; + } + const compiled = { + validate: null, + link: null + }; + self.__compiled__[name] = compiled; + if (isObject(val)) { + if (isRegExp(val.validate)) { + compiled.validate = createValidator(val.validate); + } else if (isFunction(val.validate)) { + compiled.validate = val.validate; + } else { + schemaError(name, val); + } + if (isFunction(val.normalize)) { + compiled.normalize = val.normalize; + } else if (!val.normalize) { + compiled.normalize = createNormalizer(); + } else { + schemaError(name, val); + } + return; + } + if (isString(val)) { + aliases.push(name); + return; + } + schemaError(name, val); + }); + + // + // Compile postponed aliases + // + + aliases.forEach(function (alias) { + if (!self.__compiled__[self.__schemas__[alias]]) { + // Silently fail on missed schemas to avoid errons on disable. + // schemaError(alias, self.__schemas__[alias]); + return; + } + self.__compiled__[alias].validate = self.__compiled__[self.__schemas__[alias]].validate; + self.__compiled__[alias].normalize = self.__compiled__[self.__schemas__[alias]].normalize; + }); + + // + // Fake record for guessed links + // + self.__compiled__[''] = { + validate: null, + normalize: createNormalizer() + }; + + // + // Build schema condition + // + const slist = Object.keys(self.__compiled__).filter(function (name) { + // Filter disabled & fake schemas + return name.length > 0 && self.__compiled__[name]; + }).map(escapeRE).join('|'); + // (?!_) cause 1.5x slowdown + self.re.schema_test = RegExp('(^|(?!_)(?:[><\uff5c]|' + re.src_ZPCc + '))(' + slist + ')', 'i'); + self.re.schema_search = RegExp('(^|(?!_)(?:[><\uff5c]|' + re.src_ZPCc + '))(' + slist + ')', 'ig'); + self.re.schema_at_start = RegExp('^' + self.re.schema_search.source, 'i'); + self.re.pretest = RegExp('(' + self.re.schema_test.source + ')|(' + self.re.host_fuzzy_test.source + ')|@', 'i'); + + // + // Cleanup + // + + resetScanCache(self); +} + +/** + * class Match + * + * Match result. Single element of array, returned by [[LinkifyIt#match]] + **/ +function Match(self, shift) { + const start = self.__index__; + const end = self.__last_index__; + const text = self.__text_cache__.slice(start, end); + + /** + * Match#schema -> String + * + * Prefix (protocol) for matched string. + **/ + this.schema = self.__schema__.toLowerCase(); + /** + * Match#index -> Number + * + * First position of matched string. + **/ + this.index = start + shift; + /** + * Match#lastIndex -> Number + * + * Next position after matched string. + **/ + this.lastIndex = end + shift; + /** + * Match#raw -> String + * + * Matched string. + **/ + this.raw = text; + /** + * Match#text -> String + * + * Notmalized text of matched string. + **/ + this.text = text; + /** + * Match#url -> String + * + * Normalized url of matched string. + **/ + this.url = text; +} +function createMatch(self, shift) { + const match = new Match(self, shift); + self.__compiled__[match.schema].normalize(match, self); + return match; +} + +/** + * class LinkifyIt + **/ + +/** + * new LinkifyIt(schemas, options) + * - schemas (Object): Optional. Additional schemas to validate (prefix/validator) + * - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false } + * + * Creates new linkifier instance with optional additional schemas. + * Can be called without `new` keyword for convenience. + * + * By default understands: + * + * - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links + * - "fuzzy" links and emails (example.com, foo@bar.com). + * + * `schemas` is an object, where each key/value describes protocol/rule: + * + * - __key__ - link prefix (usually, protocol name with `:` at the end, `skype:` + * for example). `linkify-it` makes shure that prefix is not preceeded with + * alphanumeric char and symbols. Only whitespaces and punctuation allowed. + * - __value__ - rule to check tail after link prefix + * - _String_ - just alias to existing rule + * - _Object_ + * - _validate_ - validator function (should return matched length on success), + * or `RegExp`. + * - _normalize_ - optional function to normalize text & url of matched result + * (for example, for @twitter mentions). + * + * `options`: + * + * - __fuzzyLink__ - recognige URL-s without `http(s):` prefix. Default `true`. + * - __fuzzyIP__ - allow IPs in fuzzy links above. Can conflict with some texts + * like version numbers. Default `false`. + * - __fuzzyEmail__ - recognize emails without `mailto:` prefix. + * + **/ +function LinkifyIt(schemas, options) { + if (!(this instanceof LinkifyIt)) { + return new LinkifyIt(schemas, options); + } + if (!options) { + if (isOptionsObj(schemas)) { + options = schemas; + schemas = {}; + } + } + this.__opts__ = assign({}, defaultOptions, options); + + // Cache last tested result. Used to skip repeating steps on next `match` call. + this.__index__ = -1; + this.__last_index__ = -1; // Next scan position + this.__schema__ = ''; + this.__text_cache__ = ''; + this.__schemas__ = assign({}, defaultSchemas, schemas); + this.__compiled__ = {}; + this.__tlds__ = tlds_default; + this.__tlds_replaced__ = false; + this.re = {}; + compile(this); +} + +/** chainable + * LinkifyIt#add(schema, definition) + * - schema (String): rule name (fixed pattern prefix) + * - definition (String|RegExp|Object): schema definition + * + * Add new rule definition. See constructor description for details. + **/ +LinkifyIt.prototype.add = function add(schema, definition) { + this.__schemas__[schema] = definition; + compile(this); + return this; +}; + +/** chainable + * LinkifyIt#set(options) + * - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false } + * + * Set recognition options for links without schema. + **/ +LinkifyIt.prototype.set = function set(options) { + this.__opts__ = assign(this.__opts__, options); + return this; +}; + +/** + * LinkifyIt#test(text) -> Boolean + * + * Searches linkifiable pattern and returns `true` on success or `false` on fail. + **/ +LinkifyIt.prototype.test = function test(text) { + // Reset scan cache + this.__text_cache__ = text; + this.__index__ = -1; + if (!text.length) { + return false; + } + let m, ml, me, len, shift, next, re, tld_pos, at_pos; + + // try to scan for link with schema - that's the most simple rule + if (this.re.schema_test.test(text)) { + re = this.re.schema_search; + re.lastIndex = 0; + while ((m = re.exec(text)) !== null) { + len = this.testSchemaAt(text, m[2], re.lastIndex); + if (len) { + this.__schema__ = m[2]; + this.__index__ = m.index + m[1].length; + this.__last_index__ = m.index + m[0].length + len; + break; + } + } + } + if (this.__opts__.fuzzyLink && this.__compiled__['http:']) { + // guess schemaless links + tld_pos = text.search(this.re.host_fuzzy_test); + if (tld_pos >= 0) { + // if tld is located after found link - no need to check fuzzy pattern + if (this.__index__ < 0 || tld_pos < this.__index__) { + if ((ml = text.match(this.__opts__.fuzzyIP ? this.re.link_fuzzy : this.re.link_no_ip_fuzzy)) !== null) { + shift = ml.index + ml[1].length; + if (this.__index__ < 0 || shift < this.__index__) { + this.__schema__ = ''; + this.__index__ = shift; + this.__last_index__ = ml.index + ml[0].length; + } + } + } + } + } + if (this.__opts__.fuzzyEmail && this.__compiled__['mailto:']) { + // guess schemaless emails + at_pos = text.indexOf('@'); + if (at_pos >= 0) { + // We can't skip this check, because this cases are possible: + // 192.168.1.1@gmail.com, my.in@example.com + if ((me = text.match(this.re.email_fuzzy)) !== null) { + shift = me.index + me[1].length; + next = me.index + me[0].length; + if (this.__index__ < 0 || shift < this.__index__ || shift === this.__index__ && next > this.__last_index__) { + this.__schema__ = 'mailto:'; + this.__index__ = shift; + this.__last_index__ = next; + } + } + } + } + return this.__index__ >= 0; +}; + +/** + * LinkifyIt#pretest(text) -> Boolean + * + * Very quick check, that can give false positives. Returns true if link MAY BE + * can exists. Can be used for speed optimization, when you need to check that + * link NOT exists. + **/ +LinkifyIt.prototype.pretest = function pretest(text) { + return this.re.pretest.test(text); +}; + +/** + * LinkifyIt#testSchemaAt(text, name, position) -> Number + * - text (String): text to scan + * - name (String): rule (schema) name + * - position (Number): text offset to check from + * + * Similar to [[LinkifyIt#test]] but checks only specific protocol tail exactly + * at given position. Returns length of found pattern (0 on fail). + **/ +LinkifyIt.prototype.testSchemaAt = function testSchemaAt(text, schema, pos) { + // If not supported schema check requested - terminate + if (!this.__compiled__[schema.toLowerCase()]) { + return 0; + } + return this.__compiled__[schema.toLowerCase()].validate(text, pos, this); +}; + +/** + * LinkifyIt#match(text) -> Array|null + * + * Returns array of found link descriptions or `null` on fail. We strongly + * recommend to use [[LinkifyIt#test]] first, for best speed. + * + * ##### Result match description + * + * - __schema__ - link schema, can be empty for fuzzy links, or `//` for + * protocol-neutral links. + * - __index__ - offset of matched text + * - __lastIndex__ - index of next char after mathch end + * - __raw__ - matched text + * - __text__ - normalized text + * - __url__ - link, generated from matched text + **/ +LinkifyIt.prototype.match = function match(text) { + const result = []; + let shift = 0; + + // Try to take previous element from cache, if .test() called before + if (this.__index__ >= 0 && this.__text_cache__ === text) { + result.push(createMatch(this, shift)); + shift = this.__last_index__; + } + + // Cut head if cache was used + let tail = shift ? text.slice(shift) : text; + + // Scan string until end reached + while (this.test(tail)) { + result.push(createMatch(this, shift)); + tail = tail.slice(this.__last_index__); + shift += this.__last_index__; + } + if (result.length) { + return result; + } + return null; +}; + +/** + * LinkifyIt#matchAtStart(text) -> Match|null + * + * Returns fully-formed (not fuzzy) link if it starts at the beginning + * of the string, and null otherwise. + **/ +LinkifyIt.prototype.matchAtStart = function matchAtStart(text) { + // Reset scan cache + this.__text_cache__ = text; + this.__index__ = -1; + if (!text.length) return null; + const m = this.re.schema_at_start.exec(text); + if (!m) return null; + const len = this.testSchemaAt(text, m[2], m[0].length); + if (!len) return null; + this.__schema__ = m[2]; + this.__index__ = m.index + m[1].length; + this.__last_index__ = m.index + m[0].length + len; + return createMatch(this, 0); +}; + +/** chainable + * LinkifyIt#tlds(list [, keepOld]) -> this + * - list (Array): list of tlds + * - keepOld (Boolean): merge with current list if `true` (`false` by default) + * + * Load (or merge) new tlds list. Those are user for fuzzy links (without prefix) + * to avoid false positives. By default this algorythm used: + * + * - hostname with any 2-letter root zones are ok. + * - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф + * are ok. + * - encoded (`xn--...`) root zones are ok. + * + * If list is replaced, then exact match for 2-chars root zones will be checked. + **/ +LinkifyIt.prototype.tlds = function tlds(list, keepOld) { + list = Array.isArray(list) ? list : [list]; + if (!keepOld) { + this.__tlds__ = list.slice(); + this.__tlds_replaced__ = true; + compile(this); + return this; + } + this.__tlds__ = this.__tlds__.concat(list).sort().filter(function (el, idx, arr) { + return el !== arr[idx - 1]; + }).reverse(); + compile(this); + return this; +}; + +/** + * LinkifyIt#normalize(match) + * + * Default normalizer (if schema does not define it's own). + **/ +LinkifyIt.prototype.normalize = function normalize(match) { + // Do minimal possible changes by default. Need to collect feedback prior + // to move forward https://github.com/markdown-it/linkify-it/issues/1 + + if (!match.schema) { + match.url = 'http://' + match.url; + } + if (match.schema === 'mailto:' && !/^mailto:/i.test(match.url)) { + match.url = 'mailto:' + match.url; + } +}; + +/** + * LinkifyIt#onCompile() + * + * Override to modify basic RegExp-s. + **/ +LinkifyIt.prototype.onCompile = function onCompile() {}; +module.exports = LinkifyIt; + +/***/ }), + +/***/ "../node_modules/markdown-it/dist/index.cjs.js": +/*!*****************************************************!*\ + !*** ../node_modules/markdown-it/dist/index.cjs.js ***! + \*****************************************************/ +/***/ (function(module, __unused_webpack_exports, __webpack_require__) { + + + +var mdurl = __webpack_require__(/*! mdurl */ "../node_modules/mdurl/build/index.cjs.js"); +var ucmicro = __webpack_require__(/*! uc.micro */ "../node_modules/uc.micro/build/index.cjs.js"); +var entities = __webpack_require__(/*! entities */ "../../../node_modules/entities/lib/index.js"); +var LinkifyIt = __webpack_require__(/*! linkify-it */ "../node_modules/linkify-it/build/index.cjs.js"); +var punycode = __webpack_require__(/*! punycode.js */ "../../../node_modules/punycode.js/punycode.es6.js"); +function _interopNamespaceDefault(e) { + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { + return e[k]; + } + }); + } + }); + } + n.default = e; + return Object.freeze(n); +} +var mdurl__namespace = /*#__PURE__*/_interopNamespaceDefault(mdurl); +var ucmicro__namespace = /*#__PURE__*/_interopNamespaceDefault(ucmicro); + +// Utilities +// + +function _class(obj) { + return Object.prototype.toString.call(obj); +} +function isString(obj) { + return _class(obj) === '[object String]'; +} +const _hasOwnProperty = Object.prototype.hasOwnProperty; +function has(object, key) { + return _hasOwnProperty.call(object, key); +} + +// Merge objects +// +function assign(obj /* from1, from2, from3, ... */) { + const sources = Array.prototype.slice.call(arguments, 1); + sources.forEach(function (source) { + if (!source) { + return; + } + if (typeof source !== 'object') { + throw new TypeError(source + 'must be object'); + } + Object.keys(source).forEach(function (key) { + obj[key] = source[key]; + }); + }); + return obj; +} + +// Remove element from array and put another array at those position. +// Useful for some operations with tokens +function arrayReplaceAt(src, pos, newElements) { + return [].concat(src.slice(0, pos), newElements, src.slice(pos + 1)); +} +function isValidEntityCode(c) { + /* eslint no-bitwise:0 */ + // broken sequence + if (c >= 0xD800 && c <= 0xDFFF) { + return false; + } + // never used + if (c >= 0xFDD0 && c <= 0xFDEF) { + return false; + } + if ((c & 0xFFFF) === 0xFFFF || (c & 0xFFFF) === 0xFFFE) { + return false; + } + // control codes + if (c >= 0x00 && c <= 0x08) { + return false; + } + if (c === 0x0B) { + return false; + } + if (c >= 0x0E && c <= 0x1F) { + return false; + } + if (c >= 0x7F && c <= 0x9F) { + return false; + } + // out of range + if (c > 0x10FFFF) { + return false; + } + return true; +} +function fromCodePoint(c) { + /* eslint no-bitwise:0 */ + if (c > 0xffff) { + c -= 0x10000; + const surrogate1 = 0xd800 + (c >> 10); + const surrogate2 = 0xdc00 + (c & 0x3ff); + return String.fromCharCode(surrogate1, surrogate2); + } + return String.fromCharCode(c); +} +const UNESCAPE_MD_RE = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g; +const ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi; +const UNESCAPE_ALL_RE = new RegExp(UNESCAPE_MD_RE.source + '|' + ENTITY_RE.source, 'gi'); +const DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$/i; +function replaceEntityPattern(match, name) { + if (name.charCodeAt(0) === 0x23 /* # */ && DIGITAL_ENTITY_TEST_RE.test(name)) { + const code = name[1].toLowerCase() === 'x' ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10); + if (isValidEntityCode(code)) { + return fromCodePoint(code); + } + return match; + } + const decoded = entities.decodeHTML(match); + if (decoded !== match) { + return decoded; + } + return match; +} + +/* function replaceEntities(str) { + if (str.indexOf('&') < 0) { return str; } + + return str.replace(ENTITY_RE, replaceEntityPattern); +} */ + +function unescapeMd(str) { + if (str.indexOf('\\') < 0) { + return str; + } + return str.replace(UNESCAPE_MD_RE, '$1'); +} +function unescapeAll(str) { + if (str.indexOf('\\') < 0 && str.indexOf('&') < 0) { + return str; + } + return str.replace(UNESCAPE_ALL_RE, function (match, escaped, entity) { + if (escaped) { + return escaped; + } + return replaceEntityPattern(match, entity); + }); +} +const HTML_ESCAPE_TEST_RE = /[&<>"]/; +const HTML_ESCAPE_REPLACE_RE = /[&<>"]/g; +const HTML_REPLACEMENTS = { + '&': '&', + '<': '<', + '>': '>', + '"': '"' +}; +function replaceUnsafeChar(ch) { + return HTML_REPLACEMENTS[ch]; +} +function escapeHtml(str) { + if (HTML_ESCAPE_TEST_RE.test(str)) { + return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar); + } + return str; +} +const REGEXP_ESCAPE_RE = /[.?*+^$[\]\\(){}|-]/g; +function escapeRE(str) { + return str.replace(REGEXP_ESCAPE_RE, '\\$&'); +} +function isSpace(code) { + switch (code) { + case 0x09: + case 0x20: + return true; + } + return false; +} + +// Zs (unicode class) || [\t\f\v\r\n] +function isWhiteSpace(code) { + if (code >= 0x2000 && code <= 0x200A) { + return true; + } + switch (code) { + case 0x09: // \t + case 0x0A: // \n + case 0x0B: // \v + case 0x0C: // \f + case 0x0D: // \r + case 0x20: + case 0xA0: + case 0x1680: + case 0x202F: + case 0x205F: + case 0x3000: + return true; + } + return false; +} + +/* eslint-disable max-len */ + +// Currently without astral characters support. +function isPunctChar(ch) { + return ucmicro__namespace.P.test(ch) || ucmicro__namespace.S.test(ch); +} + +// Markdown ASCII punctuation characters. +// +// !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~ +// http://spec.commonmark.org/0.15/#ascii-punctuation-character +// +// Don't confuse with unicode punctuation !!! It lacks some chars in ascii range. +// +function isMdAsciiPunct(ch) { + switch (ch) { + case 0x21 /* ! */: + case 0x22 /* " */: + case 0x23 /* # */: + case 0x24 /* $ */: + case 0x25 /* % */: + case 0x26 /* & */: + case 0x27 /* ' */: + case 0x28 /* ( */: + case 0x29 /* ) */: + case 0x2A /* * */: + case 0x2B /* + */: + case 0x2C /* , */: + case 0x2D /* - */: + case 0x2E /* . */: + case 0x2F /* / */: + case 0x3A /* : */: + case 0x3B /* ; */: + case 0x3C /* < */: + case 0x3D /* = */: + case 0x3E /* > */: + case 0x3F /* ? */: + case 0x40 /* @ */: + case 0x5B /* [ */: + case 0x5C /* \ */: + case 0x5D /* ] */: + case 0x5E /* ^ */: + case 0x5F /* _ */: + case 0x60 /* ` */: + case 0x7B /* { */: + case 0x7C /* | */: + case 0x7D /* } */: + case 0x7E /* ~ */: + return true; + default: + return false; + } +} + +// Hepler to unify [reference labels]. +// +function normalizeReference(str) { + // Trim and collapse whitespace + // + str = str.trim().replace(/\s+/g, ' '); + + // In node v10 'ẞ'.toLowerCase() === 'Ṿ', which is presumed to be a bug + // fixed in v12 (couldn't find any details). + // + // So treat this one as a special case + // (remove this when node v10 is no longer supported). + // + if ('ẞ'.toLowerCase() === 'Ṿ') { + str = str.replace(/ẞ/g, 'ß'); + } + + // .toLowerCase().toUpperCase() should get rid of all differences + // between letter variants. + // + // Simple .toLowerCase() doesn't normalize 125 code points correctly, + // and .toUpperCase doesn't normalize 6 of them (list of exceptions: + // İ, ϴ, ẞ, Ω, K, Å - those are already uppercased, but have differently + // uppercased versions). + // + // Here's an example showing how it happens. Lets take greek letter omega: + // uppercase U+0398 (Θ), U+03f4 (ϴ) and lowercase U+03b8 (θ), U+03d1 (ϑ) + // + // Unicode entries: + // 0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8; + // 03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398 + // 03D1;GREEK THETA SYMBOL;Ll;0;L; 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398 + // 03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L; 0398;;;;N;;;;03B8; + // + // Case-insensitive comparison should treat all of them as equivalent. + // + // But .toLowerCase() doesn't change ϑ (it's already lowercase), + // and .toUpperCase() doesn't change ϴ (already uppercase). + // + // Applying first lower then upper case normalizes any character: + // '\u0398\u03f4\u03b8\u03d1'.toLowerCase().toUpperCase() === '\u0398\u0398\u0398\u0398' + // + // Note: this is equivalent to unicode case folding; unicode normalization + // is a different step that is not required here. + // + // Final result should be uppercased, because it's later stored in an object + // (this avoid a conflict with Object.prototype members, + // most notably, `__proto__`) + // + return str.toLowerCase().toUpperCase(); +} + +// Re-export libraries commonly used in both markdown-it and its plugins, +// so plugins won't have to depend on them explicitly, which reduces their +// bundled size (e.g. a browser build). +// +const lib = { + mdurl: mdurl__namespace, + ucmicro: ucmicro__namespace +}; +var utils = /*#__PURE__*/Object.freeze({ + __proto__: null, + arrayReplaceAt: arrayReplaceAt, + assign: assign, + escapeHtml: escapeHtml, + escapeRE: escapeRE, + fromCodePoint: fromCodePoint, + has: has, + isMdAsciiPunct: isMdAsciiPunct, + isPunctChar: isPunctChar, + isSpace: isSpace, + isString: isString, + isValidEntityCode: isValidEntityCode, + isWhiteSpace: isWhiteSpace, + lib: lib, + normalizeReference: normalizeReference, + unescapeAll: unescapeAll, + unescapeMd: unescapeMd +}); + +// Parse link label +// +// this function assumes that first character ("[") already matches; +// returns the end of the label +// + +function parseLinkLabel(state, start, disableNested) { + let level, found, marker, prevPos; + const max = state.posMax; + const oldPos = state.pos; + state.pos = start + 1; + level = 1; + while (state.pos < max) { + marker = state.src.charCodeAt(state.pos); + if (marker === 0x5D /* ] */) { + level--; + if (level === 0) { + found = true; + break; + } + } + prevPos = state.pos; + state.md.inline.skipToken(state); + if (marker === 0x5B /* [ */) { + if (prevPos === state.pos - 1) { + // increase level if we find text `[`, which is not a part of any token + level++; + } else if (disableNested) { + state.pos = oldPos; + return -1; + } + } + } + let labelEnd = -1; + if (found) { + labelEnd = state.pos; + } + + // restore old state + state.pos = oldPos; + return labelEnd; +} + +// Parse link destination +// + +function parseLinkDestination(str, start, max) { + let code; + let pos = start; + const result = { + ok: false, + pos: 0, + str: '' + }; + if (str.charCodeAt(pos) === 0x3C /* < */) { + pos++; + while (pos < max) { + code = str.charCodeAt(pos); + if (code === 0x0A /* \n */) { + return result; + } + if (code === 0x3C /* < */) { + return result; + } + if (code === 0x3E /* > */) { + result.pos = pos + 1; + result.str = unescapeAll(str.slice(start + 1, pos)); + result.ok = true; + return result; + } + if (code === 0x5C /* \ */ && pos + 1 < max) { + pos += 2; + continue; + } + pos++; + } + + // no closing '>' + return result; + } + + // this should be ... } else { ... branch + + let level = 0; + while (pos < max) { + code = str.charCodeAt(pos); + if (code === 0x20) { + break; + } + + // ascii control characters + if (code < 0x20 || code === 0x7F) { + break; + } + if (code === 0x5C /* \ */ && pos + 1 < max) { + if (str.charCodeAt(pos + 1) === 0x20) { + break; + } + pos += 2; + continue; + } + if (code === 0x28 /* ( */) { + level++; + if (level > 32) { + return result; + } + } + if (code === 0x29 /* ) */) { + if (level === 0) { + break; + } + level--; + } + pos++; + } + if (start === pos) { + return result; + } + if (level !== 0) { + return result; + } + result.str = unescapeAll(str.slice(start, pos)); + result.pos = pos; + result.ok = true; + return result; +} + +// Parse link title +// + +// Parse link title within `str` in [start, max] range, +// or continue previous parsing if `prev_state` is defined (equal to result of last execution). +// +function parseLinkTitle(str, start, max, prev_state) { + let code; + let pos = start; + const state = { + // if `true`, this is a valid link title + ok: false, + // if `true`, this link can be continued on the next line + can_continue: false, + // if `ok`, it's the position of the first character after the closing marker + pos: 0, + // if `ok`, it's the unescaped title + str: '', + // expected closing marker character code + marker: 0 + }; + if (prev_state) { + // this is a continuation of a previous parseLinkTitle call on the next line, + // used in reference links only + state.str = prev_state.str; + state.marker = prev_state.marker; + } else { + if (pos >= max) { + return state; + } + let marker = str.charCodeAt(pos); + if (marker !== 0x22 /* " */ && marker !== 0x27 /* ' */ && marker !== 0x28 /* ( */) { + return state; + } + start++; + pos++; + + // if opening marker is "(", switch it to closing marker ")" + if (marker === 0x28) { + marker = 0x29; + } + state.marker = marker; + } + while (pos < max) { + code = str.charCodeAt(pos); + if (code === state.marker) { + state.pos = pos + 1; + state.str += unescapeAll(str.slice(start, pos)); + state.ok = true; + return state; + } else if (code === 0x28 /* ( */ && state.marker === 0x29 /* ) */) { + return state; + } else if (code === 0x5C /* \ */ && pos + 1 < max) { + pos++; + } + pos++; + } + + // no closing marker found, but this link title may continue on the next line (for references) + state.can_continue = true; + state.str += unescapeAll(str.slice(start, pos)); + return state; +} + +// Just a shortcut for bulk export + +var helpers = /*#__PURE__*/Object.freeze({ + __proto__: null, + parseLinkDestination: parseLinkDestination, + parseLinkLabel: parseLinkLabel, + parseLinkTitle: parseLinkTitle +}); + +/** + * class Renderer + * + * Generates HTML from parsed token stream. Each instance has independent + * copy of rules. Those can be rewritten with ease. Also, you can add new + * rules if you create plugin and adds new token types. + **/ + +const default_rules = {}; +default_rules.code_inline = function (tokens, idx, options, env, slf) { + const token = tokens[idx]; + return '' + escapeHtml(token.content) + ''; +}; +default_rules.code_block = function (tokens, idx, options, env, slf) { + const token = tokens[idx]; + return '' + escapeHtml(tokens[idx].content) + '\n'; +}; +default_rules.fence = function (tokens, idx, options, env, slf) { + const token = tokens[idx]; + const info = token.info ? unescapeAll(token.info).trim() : ''; + let langName = ''; + let langAttrs = ''; + if (info) { + const arr = info.split(/(\s+)/g); + langName = arr[0]; + langAttrs = arr.slice(2).join(''); + } + let highlighted; + if (options.highlight) { + highlighted = options.highlight(token.content, langName, langAttrs) || escapeHtml(token.content); + } else { + highlighted = escapeHtml(token.content); + } + if (highlighted.indexOf('${highlighted}\n`; + } + return `
    ${highlighted}
    \n`; +}; +default_rules.image = function (tokens, idx, options, env, slf) { + const token = tokens[idx]; + + // "alt" attr MUST be set, even if empty. Because it's mandatory and + // should be placed on proper position for tests. + // + // Replace content with actual value + + token.attrs[token.attrIndex('alt')][1] = slf.renderInlineAsText(token.children, options, env); + return slf.renderToken(tokens, idx, options); +}; +default_rules.hardbreak = function (tokens, idx, options /*, env */) { + return options.xhtmlOut ? '
    \n' : '
    \n'; +}; +default_rules.softbreak = function (tokens, idx, options /*, env */) { + return options.breaks ? options.xhtmlOut ? '
    \n' : '
    \n' : '\n'; +}; +default_rules.text = function (tokens, idx /*, options, env */) { + return escapeHtml(tokens[idx].content); +}; +default_rules.html_block = function (tokens, idx /*, options, env */) { + return tokens[idx].content; +}; +default_rules.html_inline = function (tokens, idx /*, options, env */) { + return tokens[idx].content; +}; + +/** + * new Renderer() + * + * Creates new [[Renderer]] instance and fill [[Renderer#rules]] with defaults. + **/ +function Renderer() { + /** + * Renderer#rules -> Object + * + * Contains render rules for tokens. Can be updated and extended. + * + * ##### Example + * + * ```javascript + * var md = require('markdown-it')(); + * + * md.renderer.rules.strong_open = function () { return ''; }; + * md.renderer.rules.strong_close = function () { return ''; }; + * + * var result = md.renderInline(...); + * ``` + * + * Each rule is called as independent static function with fixed signature: + * + * ```javascript + * function my_token_render(tokens, idx, options, env, renderer) { + * // ... + * return renderedHTML; + * } + * ``` + * + * See [source code](https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.mjs) + * for more details and examples. + **/ + this.rules = assign({}, default_rules); +} + +/** + * Renderer.renderAttrs(token) -> String + * + * Render token attributes to string. + **/ +Renderer.prototype.renderAttrs = function renderAttrs(token) { + let i, l, result; + if (!token.attrs) { + return ''; + } + result = ''; + for (i = 0, l = token.attrs.length; i < l; i++) { + result += ' ' + escapeHtml(token.attrs[i][0]) + '="' + escapeHtml(token.attrs[i][1]) + '"'; + } + return result; +}; + +/** + * Renderer.renderToken(tokens, idx, options) -> String + * - tokens (Array): list of tokens + * - idx (Numbed): token index to render + * - options (Object): params of parser instance + * + * Default token renderer. Can be overriden by custom function + * in [[Renderer#rules]]. + **/ +Renderer.prototype.renderToken = function renderToken(tokens, idx, options) { + const token = tokens[idx]; + let result = ''; + + // Tight list paragraphs + if (token.hidden) { + return ''; + } + + // Insert a newline between hidden paragraph and subsequent opening + // block-level tag. + // + // For example, here we should insert a newline before blockquote: + // - a + // > + // + if (token.block && token.nesting !== -1 && idx && tokens[idx - 1].hidden) { + result += '\n'; + } + + // Add token name, e.g. ``. + // + needLf = false; + } + } + } + } + result += needLf ? '>\n' : '>'; + return result; +}; + +/** + * Renderer.renderInline(tokens, options, env) -> String + * - tokens (Array): list on block tokens to render + * - options (Object): params of parser instance + * - env (Object): additional data from parsed input (references, for example) + * + * The same as [[Renderer.render]], but for single token of `inline` type. + **/ +Renderer.prototype.renderInline = function (tokens, options, env) { + let result = ''; + const rules = this.rules; + for (let i = 0, len = tokens.length; i < len; i++) { + const type = tokens[i].type; + if (typeof rules[type] !== 'undefined') { + result += rules[type](tokens, i, options, env, this); + } else { + result += this.renderToken(tokens, i, options); + } + } + return result; +}; + +/** internal + * Renderer.renderInlineAsText(tokens, options, env) -> String + * - tokens (Array): list on block tokens to render + * - options (Object): params of parser instance + * - env (Object): additional data from parsed input (references, for example) + * + * Special kludge for image `alt` attributes to conform CommonMark spec. + * Don't try to use it! Spec requires to show `alt` content with stripped markup, + * instead of simple escaping. + **/ +Renderer.prototype.renderInlineAsText = function (tokens, options, env) { + let result = ''; + for (let i = 0, len = tokens.length; i < len; i++) { + switch (tokens[i].type) { + case 'text': + result += tokens[i].content; + break; + case 'image': + result += this.renderInlineAsText(tokens[i].children, options, env); + break; + case 'html_inline': + case 'html_block': + result += tokens[i].content; + break; + case 'softbreak': + case 'hardbreak': + result += '\n'; + break; + // all other tokens are skipped + } + } + + return result; +}; + +/** + * Renderer.render(tokens, options, env) -> String + * - tokens (Array): list on block tokens to render + * - options (Object): params of parser instance + * - env (Object): additional data from parsed input (references, for example) + * + * Takes token stream and generates HTML. Probably, you will never need to call + * this method directly. + **/ +Renderer.prototype.render = function (tokens, options, env) { + let result = ''; + const rules = this.rules; + for (let i = 0, len = tokens.length; i < len; i++) { + const type = tokens[i].type; + if (type === 'inline') { + result += this.renderInline(tokens[i].children, options, env); + } else if (typeof rules[type] !== 'undefined') { + result += rules[type](tokens, i, options, env, this); + } else { + result += this.renderToken(tokens, i, options, env); + } + } + return result; +}; + +/** + * class Ruler + * + * Helper class, used by [[MarkdownIt#core]], [[MarkdownIt#block]] and + * [[MarkdownIt#inline]] to manage sequences of functions (rules): + * + * - keep rules in defined order + * - assign the name to each rule + * - enable/disable rules + * - add/replace rules + * - allow assign rules to additional named chains (in the same) + * - cacheing lists of active rules + * + * You will not need use this class directly until write plugins. For simple + * rules control use [[MarkdownIt.disable]], [[MarkdownIt.enable]] and + * [[MarkdownIt.use]]. + **/ + +/** + * new Ruler() + **/ +function Ruler() { + // List of added rules. Each element is: + // + // { + // name: XXX, + // enabled: Boolean, + // fn: Function(), + // alt: [ name2, name3 ] + // } + // + this.__rules__ = []; + + // Cached rule chains. + // + // First level - chain name, '' for default. + // Second level - diginal anchor for fast filtering by charcodes. + // + this.__cache__ = null; +} + +// Helper methods, should not be used directly + +// Find rule index by name +// +Ruler.prototype.__find__ = function (name) { + for (let i = 0; i < this.__rules__.length; i++) { + if (this.__rules__[i].name === name) { + return i; + } + } + return -1; +}; + +// Build rules lookup cache +// +Ruler.prototype.__compile__ = function () { + const self = this; + const chains = ['']; + + // collect unique names + self.__rules__.forEach(function (rule) { + if (!rule.enabled) { + return; + } + rule.alt.forEach(function (altName) { + if (chains.indexOf(altName) < 0) { + chains.push(altName); + } + }); + }); + self.__cache__ = {}; + chains.forEach(function (chain) { + self.__cache__[chain] = []; + self.__rules__.forEach(function (rule) { + if (!rule.enabled) { + return; + } + if (chain && rule.alt.indexOf(chain) < 0) { + return; + } + self.__cache__[chain].push(rule.fn); + }); + }); +}; + +/** + * Ruler.at(name, fn [, options]) + * - name (String): rule name to replace. + * - fn (Function): new rule function. + * - options (Object): new rule options (not mandatory). + * + * Replace rule by name with new function & options. Throws error if name not + * found. + * + * ##### Options: + * + * - __alt__ - array with names of "alternate" chains. + * + * ##### Example + * + * Replace existing typographer replacement rule with new one: + * + * ```javascript + * var md = require('markdown-it')(); + * + * md.core.ruler.at('replacements', function replace(state) { + * //... + * }); + * ``` + **/ +Ruler.prototype.at = function (name, fn, options) { + const index = this.__find__(name); + const opt = options || {}; + if (index === -1) { + throw new Error('Parser rule not found: ' + name); + } + this.__rules__[index].fn = fn; + this.__rules__[index].alt = opt.alt || []; + this.__cache__ = null; +}; + +/** + * Ruler.before(beforeName, ruleName, fn [, options]) + * - beforeName (String): new rule will be added before this one. + * - ruleName (String): name of added rule. + * - fn (Function): rule function. + * - options (Object): rule options (not mandatory). + * + * Add new rule to chain before one with given name. See also + * [[Ruler.after]], [[Ruler.push]]. + * + * ##### Options: + * + * - __alt__ - array with names of "alternate" chains. + * + * ##### Example + * + * ```javascript + * var md = require('markdown-it')(); + * + * md.block.ruler.before('paragraph', 'my_rule', function replace(state) { + * //... + * }); + * ``` + **/ +Ruler.prototype.before = function (beforeName, ruleName, fn, options) { + const index = this.__find__(beforeName); + const opt = options || {}; + if (index === -1) { + throw new Error('Parser rule not found: ' + beforeName); + } + this.__rules__.splice(index, 0, { + name: ruleName, + enabled: true, + fn, + alt: opt.alt || [] + }); + this.__cache__ = null; +}; + +/** + * Ruler.after(afterName, ruleName, fn [, options]) + * - afterName (String): new rule will be added after this one. + * - ruleName (String): name of added rule. + * - fn (Function): rule function. + * - options (Object): rule options (not mandatory). + * + * Add new rule to chain after one with given name. See also + * [[Ruler.before]], [[Ruler.push]]. + * + * ##### Options: + * + * - __alt__ - array with names of "alternate" chains. + * + * ##### Example + * + * ```javascript + * var md = require('markdown-it')(); + * + * md.inline.ruler.after('text', 'my_rule', function replace(state) { + * //... + * }); + * ``` + **/ +Ruler.prototype.after = function (afterName, ruleName, fn, options) { + const index = this.__find__(afterName); + const opt = options || {}; + if (index === -1) { + throw new Error('Parser rule not found: ' + afterName); + } + this.__rules__.splice(index + 1, 0, { + name: ruleName, + enabled: true, + fn, + alt: opt.alt || [] + }); + this.__cache__ = null; +}; + +/** + * Ruler.push(ruleName, fn [, options]) + * - ruleName (String): name of added rule. + * - fn (Function): rule function. + * - options (Object): rule options (not mandatory). + * + * Push new rule to the end of chain. See also + * [[Ruler.before]], [[Ruler.after]]. + * + * ##### Options: + * + * - __alt__ - array with names of "alternate" chains. + * + * ##### Example + * + * ```javascript + * var md = require('markdown-it')(); + * + * md.core.ruler.push('my_rule', function replace(state) { + * //... + * }); + * ``` + **/ +Ruler.prototype.push = function (ruleName, fn, options) { + const opt = options || {}; + this.__rules__.push({ + name: ruleName, + enabled: true, + fn, + alt: opt.alt || [] + }); + this.__cache__ = null; +}; + +/** + * Ruler.enable(list [, ignoreInvalid]) -> Array + * - list (String|Array): list of rule names to enable. + * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. + * + * Enable rules with given names. If any rule name not found - throw Error. + * Errors can be disabled by second param. + * + * Returns list of found rule names (if no exception happened). + * + * See also [[Ruler.disable]], [[Ruler.enableOnly]]. + **/ +Ruler.prototype.enable = function (list, ignoreInvalid) { + if (!Array.isArray(list)) { + list = [list]; + } + const result = []; + + // Search by name and enable + list.forEach(function (name) { + const idx = this.__find__(name); + if (idx < 0) { + if (ignoreInvalid) { + return; + } + throw new Error('Rules manager: invalid rule name ' + name); + } + this.__rules__[idx].enabled = true; + result.push(name); + }, this); + this.__cache__ = null; + return result; +}; + +/** + * Ruler.enableOnly(list [, ignoreInvalid]) + * - list (String|Array): list of rule names to enable (whitelist). + * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. + * + * Enable rules with given names, and disable everything else. If any rule name + * not found - throw Error. Errors can be disabled by second param. + * + * See also [[Ruler.disable]], [[Ruler.enable]]. + **/ +Ruler.prototype.enableOnly = function (list, ignoreInvalid) { + if (!Array.isArray(list)) { + list = [list]; + } + this.__rules__.forEach(function (rule) { + rule.enabled = false; + }); + this.enable(list, ignoreInvalid); +}; + +/** + * Ruler.disable(list [, ignoreInvalid]) -> Array + * - list (String|Array): list of rule names to disable. + * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. + * + * Disable rules with given names. If any rule name not found - throw Error. + * Errors can be disabled by second param. + * + * Returns list of found rule names (if no exception happened). + * + * See also [[Ruler.enable]], [[Ruler.enableOnly]]. + **/ +Ruler.prototype.disable = function (list, ignoreInvalid) { + if (!Array.isArray(list)) { + list = [list]; + } + const result = []; + + // Search by name and disable + list.forEach(function (name) { + const idx = this.__find__(name); + if (idx < 0) { + if (ignoreInvalid) { + return; + } + throw new Error('Rules manager: invalid rule name ' + name); + } + this.__rules__[idx].enabled = false; + result.push(name); + }, this); + this.__cache__ = null; + return result; +}; + +/** + * Ruler.getRules(chainName) -> Array + * + * Return array of active functions (rules) for given chain name. It analyzes + * rules configuration, compiles caches if not exists and returns result. + * + * Default chain name is `''` (empty string). It can't be skipped. That's + * done intentionally, to keep signature monomorphic for high speed. + **/ +Ruler.prototype.getRules = function (chainName) { + if (this.__cache__ === null) { + this.__compile__(); + } + + // Chain can be empty, if rules disabled. But we still have to return Array. + return this.__cache__[chainName] || []; +}; + +// Token class + +/** + * class Token + **/ + +/** + * new Token(type, tag, nesting) + * + * Create new token and fill passed properties. + **/ +function Token(type, tag, nesting) { + /** + * Token#type -> String + * + * Type of the token (string, e.g. "paragraph_open") + **/ + this.type = type; + + /** + * Token#tag -> String + * + * html tag name, e.g. "p" + **/ + this.tag = tag; + + /** + * Token#attrs -> Array + * + * Html attributes. Format: `[ [ name1, value1 ], [ name2, value2 ] ]` + **/ + this.attrs = null; + + /** + * Token#map -> Array + * + * Source map info. Format: `[ line_begin, line_end ]` + **/ + this.map = null; + + /** + * Token#nesting -> Number + * + * Level change (number in {-1, 0, 1} set), where: + * + * - `1` means the tag is opening + * - `0` means the tag is self-closing + * - `-1` means the tag is closing + **/ + this.nesting = nesting; + + /** + * Token#level -> Number + * + * nesting level, the same as `state.level` + **/ + this.level = 0; + + /** + * Token#children -> Array + * + * An array of child nodes (inline and img tokens) + **/ + this.children = null; + + /** + * Token#content -> String + * + * In a case of self-closing tag (code, html, fence, etc.), + * it has contents of this tag. + **/ + this.content = ''; + + /** + * Token#markup -> String + * + * '*' or '_' for emphasis, fence string for fence, etc. + **/ + this.markup = ''; + + /** + * Token#info -> String + * + * Additional information: + * + * - Info string for "fence" tokens + * - The value "auto" for autolink "link_open" and "link_close" tokens + * - The string value of the item marker for ordered-list "list_item_open" tokens + **/ + this.info = ''; + + /** + * Token#meta -> Object + * + * A place for plugins to store an arbitrary data + **/ + this.meta = null; + + /** + * Token#block -> Boolean + * + * True for block-level tokens, false for inline tokens. + * Used in renderer to calculate line breaks + **/ + this.block = false; + + /** + * Token#hidden -> Boolean + * + * If it's true, ignore this element when rendering. Used for tight lists + * to hide paragraphs. + **/ + this.hidden = false; +} + +/** + * Token.attrIndex(name) -> Number + * + * Search attribute index by name. + **/ +Token.prototype.attrIndex = function attrIndex(name) { + if (!this.attrs) { + return -1; + } + const attrs = this.attrs; + for (let i = 0, len = attrs.length; i < len; i++) { + if (attrs[i][0] === name) { + return i; + } + } + return -1; +}; + +/** + * Token.attrPush(attrData) + * + * Add `[ name, value ]` attribute to list. Init attrs if necessary + **/ +Token.prototype.attrPush = function attrPush(attrData) { + if (this.attrs) { + this.attrs.push(attrData); + } else { + this.attrs = [attrData]; + } +}; + +/** + * Token.attrSet(name, value) + * + * Set `name` attribute to `value`. Override old value if exists. + **/ +Token.prototype.attrSet = function attrSet(name, value) { + const idx = this.attrIndex(name); + const attrData = [name, value]; + if (idx < 0) { + this.attrPush(attrData); + } else { + this.attrs[idx] = attrData; + } +}; + +/** + * Token.attrGet(name) + * + * Get the value of attribute `name`, or null if it does not exist. + **/ +Token.prototype.attrGet = function attrGet(name) { + const idx = this.attrIndex(name); + let value = null; + if (idx >= 0) { + value = this.attrs[idx][1]; + } + return value; +}; + +/** + * Token.attrJoin(name, value) + * + * Join value to existing attribute via space. Or create new attribute if not + * exists. Useful to operate with token classes. + **/ +Token.prototype.attrJoin = function attrJoin(name, value) { + const idx = this.attrIndex(name); + if (idx < 0) { + this.attrPush([name, value]); + } else { + this.attrs[idx][1] = this.attrs[idx][1] + ' ' + value; + } +}; + +// Core state object +// + +function StateCore(src, md, env) { + this.src = src; + this.env = env; + this.tokens = []; + this.inlineMode = false; + this.md = md; // link to parser instance +} + +// re-export Token class to use in core rules +StateCore.prototype.Token = Token; + +// Normalize input string + +// https://spec.commonmark.org/0.29/#line-ending +const NEWLINES_RE = /\r\n?|\n/g; +const NULL_RE = /\0/g; +function normalize(state) { + let str; + + // Normalize newlines + str = state.src.replace(NEWLINES_RE, '\n'); + + // Replace NULL characters + str = str.replace(NULL_RE, '\uFFFD'); + state.src = str; +} +function block(state) { + let token; + if (state.inlineMode) { + token = new state.Token('inline', '', 0); + token.content = state.src; + token.map = [0, 1]; + token.children = []; + state.tokens.push(token); + } else { + state.md.block.parse(state.src, state.md, state.env, state.tokens); + } +} +function inline(state) { + const tokens = state.tokens; + + // Parse inlines + for (let i = 0, l = tokens.length; i < l; i++) { + const tok = tokens[i]; + if (tok.type === 'inline') { + state.md.inline.parse(tok.content, state.md, state.env, tok.children); + } + } +} + +// Replace link-like texts with link nodes. +// +// Currently restricted by `md.validateLink()` to http/https/ftp +// + +function isLinkOpen$1(str) { + return /^\s]/i.test(str); +} +function isLinkClose$1(str) { + return /^<\/a\s*>/i.test(str); +} +function linkify$1(state) { + const blockTokens = state.tokens; + if (!state.md.options.linkify) { + return; + } + for (let j = 0, l = blockTokens.length; j < l; j++) { + if (blockTokens[j].type !== 'inline' || !state.md.linkify.pretest(blockTokens[j].content)) { + continue; + } + let tokens = blockTokens[j].children; + let htmlLinkLevel = 0; + + // We scan from the end, to keep position when new tags added. + // Use reversed logic in links start/end match + for (let i = tokens.length - 1; i >= 0; i--) { + const currentToken = tokens[i]; + + // Skip content of markdown links + if (currentToken.type === 'link_close') { + i--; + while (tokens[i].level !== currentToken.level && tokens[i].type !== 'link_open') { + i--; + } + continue; + } + + // Skip content of html tag links + if (currentToken.type === 'html_inline') { + if (isLinkOpen$1(currentToken.content) && htmlLinkLevel > 0) { + htmlLinkLevel--; + } + if (isLinkClose$1(currentToken.content)) { + htmlLinkLevel++; + } + } + if (htmlLinkLevel > 0) { + continue; + } + if (currentToken.type === 'text' && state.md.linkify.test(currentToken.content)) { + const text = currentToken.content; + let links = state.md.linkify.match(text); + + // Now split string to nodes + const nodes = []; + let level = currentToken.level; + let lastPos = 0; + + // forbid escape sequence at the start of the string, + // this avoids http\://example.com/ from being linkified as + // http://example.com/ + if (links.length > 0 && links[0].index === 0 && i > 0 && tokens[i - 1].type === 'text_special') { + links = links.slice(1); + } + for (let ln = 0; ln < links.length; ln++) { + const url = links[ln].url; + const fullUrl = state.md.normalizeLink(url); + if (!state.md.validateLink(fullUrl)) { + continue; + } + let urlText = links[ln].text; + + // Linkifier might send raw hostnames like "example.com", where url + // starts with domain name. So we prepend http:// in those cases, + // and remove it afterwards. + // + if (!links[ln].schema) { + urlText = state.md.normalizeLinkText('http://' + urlText).replace(/^http:\/\//, ''); + } else if (links[ln].schema === 'mailto:' && !/^mailto:/i.test(urlText)) { + urlText = state.md.normalizeLinkText('mailto:' + urlText).replace(/^mailto:/, ''); + } else { + urlText = state.md.normalizeLinkText(urlText); + } + const pos = links[ln].index; + if (pos > lastPos) { + const token = new state.Token('text', '', 0); + token.content = text.slice(lastPos, pos); + token.level = level; + nodes.push(token); + } + const token_o = new state.Token('link_open', 'a', 1); + token_o.attrs = [['href', fullUrl]]; + token_o.level = level++; + token_o.markup = 'linkify'; + token_o.info = 'auto'; + nodes.push(token_o); + const token_t = new state.Token('text', '', 0); + token_t.content = urlText; + token_t.level = level; + nodes.push(token_t); + const token_c = new state.Token('link_close', 'a', -1); + token_c.level = --level; + token_c.markup = 'linkify'; + token_c.info = 'auto'; + nodes.push(token_c); + lastPos = links[ln].lastIndex; + } + if (lastPos < text.length) { + const token = new state.Token('text', '', 0); + token.content = text.slice(lastPos); + token.level = level; + nodes.push(token); + } + + // replace current node + blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes); + } + } + } +} + +// Simple typographic replacements +// +// (c) (C) → © +// (tm) (TM) → ™ +// (r) (R) → ® +// +- → ± +// ... → … (also ?.... → ?.., !.... → !..) +// ???????? → ???, !!!!! → !!!, `,,` → `,` +// -- → –, --- → — +// + +// TODO: +// - fractionals 1/2, 1/4, 3/4 -> ½, ¼, ¾ +// - multiplications 2 x 4 -> 2 × 4 + +const RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--/; + +// Workaround for phantomjs - need regex without /g flag, +// or root check will fail every second time +const SCOPED_ABBR_TEST_RE = /\((c|tm|r)\)/i; +const SCOPED_ABBR_RE = /\((c|tm|r)\)/ig; +const SCOPED_ABBR = { + c: '©', + r: '®', + tm: '™' +}; +function replaceFn(match, name) { + return SCOPED_ABBR[name.toLowerCase()]; +} +function replace_scoped(inlineTokens) { + let inside_autolink = 0; + for (let i = inlineTokens.length - 1; i >= 0; i--) { + const token = inlineTokens[i]; + if (token.type === 'text' && !inside_autolink) { + token.content = token.content.replace(SCOPED_ABBR_RE, replaceFn); + } + if (token.type === 'link_open' && token.info === 'auto') { + inside_autolink--; + } + if (token.type === 'link_close' && token.info === 'auto') { + inside_autolink++; + } + } +} +function replace_rare(inlineTokens) { + let inside_autolink = 0; + for (let i = inlineTokens.length - 1; i >= 0; i--) { + const token = inlineTokens[i]; + if (token.type === 'text' && !inside_autolink) { + if (RARE_RE.test(token.content)) { + token.content = token.content.replace(/\+-/g, '±') + // .., ..., ....... -> … + // but ?..... & !..... -> ?.. & !.. + .replace(/\.{2,}/g, '…').replace(/([?!])…/g, '$1..').replace(/([?!]){4,}/g, '$1$1$1').replace(/,{2,}/g, ',') + // em-dash + .replace(/(^|[^-])---(?=[^-]|$)/mg, '$1\u2014') + // en-dash + .replace(/(^|\s)--(?=\s|$)/mg, '$1\u2013').replace(/(^|[^-\s])--(?=[^-\s]|$)/mg, '$1\u2013'); + } + } + if (token.type === 'link_open' && token.info === 'auto') { + inside_autolink--; + } + if (token.type === 'link_close' && token.info === 'auto') { + inside_autolink++; + } + } +} +function replace(state) { + let blkIdx; + if (!state.md.options.typographer) { + return; + } + for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { + if (state.tokens[blkIdx].type !== 'inline') { + continue; + } + if (SCOPED_ABBR_TEST_RE.test(state.tokens[blkIdx].content)) { + replace_scoped(state.tokens[blkIdx].children); + } + if (RARE_RE.test(state.tokens[blkIdx].content)) { + replace_rare(state.tokens[blkIdx].children); + } + } +} + +// Convert straight quotation marks to typographic ones +// + +const QUOTE_TEST_RE = /['"]/; +const QUOTE_RE = /['"]/g; +const APOSTROPHE = '\u2019'; /* ’ */ + +function replaceAt(str, index, ch) { + return str.slice(0, index) + ch + str.slice(index + 1); +} +function process_inlines(tokens, state) { + let j; + const stack = []; + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + const thisLevel = tokens[i].level; + for (j = stack.length - 1; j >= 0; j--) { + if (stack[j].level <= thisLevel) { + break; + } + } + stack.length = j + 1; + if (token.type !== 'text') { + continue; + } + let text = token.content; + let pos = 0; + let max = text.length; + + /* eslint no-labels:0,block-scoped-var:0 */ + OUTER: while (pos < max) { + QUOTE_RE.lastIndex = pos; + const t = QUOTE_RE.exec(text); + if (!t) { + break; + } + let canOpen = true; + let canClose = true; + pos = t.index + 1; + const isSingle = t[0] === "'"; + + // Find previous character, + // default to space if it's the beginning of the line + // + let lastChar = 0x20; + if (t.index - 1 >= 0) { + lastChar = text.charCodeAt(t.index - 1); + } else { + for (j = i - 1; j >= 0; j--) { + if (tokens[j].type === 'softbreak' || tokens[j].type === 'hardbreak') break; // lastChar defaults to 0x20 + if (!tokens[j].content) continue; // should skip all tokens except 'text', 'html_inline' or 'code_inline' + + lastChar = tokens[j].content.charCodeAt(tokens[j].content.length - 1); + break; + } + } + + // Find next character, + // default to space if it's the end of the line + // + let nextChar = 0x20; + if (pos < max) { + nextChar = text.charCodeAt(pos); + } else { + for (j = i + 1; j < tokens.length; j++) { + if (tokens[j].type === 'softbreak' || tokens[j].type === 'hardbreak') break; // nextChar defaults to 0x20 + if (!tokens[j].content) continue; // should skip all tokens except 'text', 'html_inline' or 'code_inline' + + nextChar = tokens[j].content.charCodeAt(0); + break; + } + } + const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar)); + const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar)); + const isLastWhiteSpace = isWhiteSpace(lastChar); + const isNextWhiteSpace = isWhiteSpace(nextChar); + if (isNextWhiteSpace) { + canOpen = false; + } else if (isNextPunctChar) { + if (!(isLastWhiteSpace || isLastPunctChar)) { + canOpen = false; + } + } + if (isLastWhiteSpace) { + canClose = false; + } else if (isLastPunctChar) { + if (!(isNextWhiteSpace || isNextPunctChar)) { + canClose = false; + } + } + if (nextChar === 0x22 /* " */ && t[0] === '"') { + if (lastChar >= 0x30 /* 0 */ && lastChar <= 0x39 /* 9 */) { + // special case: 1"" - count first quote as an inch + canClose = canOpen = false; + } + } + if (canOpen && canClose) { + // Replace quotes in the middle of punctuation sequence, but not + // in the middle of the words, i.e.: + // + // 1. foo " bar " baz - not replaced + // 2. foo-"-bar-"-baz - replaced + // 3. foo"bar"baz - not replaced + // + canOpen = isLastPunctChar; + canClose = isNextPunctChar; + } + if (!canOpen && !canClose) { + // middle of word + if (isSingle) { + token.content = replaceAt(token.content, t.index, APOSTROPHE); + } + continue; + } + if (canClose) { + // this could be a closing quote, rewind the stack to get a match + for (j = stack.length - 1; j >= 0; j--) { + let item = stack[j]; + if (stack[j].level < thisLevel) { + break; + } + if (item.single === isSingle && stack[j].level === thisLevel) { + item = stack[j]; + let openQuote; + let closeQuote; + if (isSingle) { + openQuote = state.md.options.quotes[2]; + closeQuote = state.md.options.quotes[3]; + } else { + openQuote = state.md.options.quotes[0]; + closeQuote = state.md.options.quotes[1]; + } + + // replace token.content *before* tokens[item.token].content, + // because, if they are pointing at the same token, replaceAt + // could mess up indices when quote length != 1 + token.content = replaceAt(token.content, t.index, closeQuote); + tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, openQuote); + pos += closeQuote.length - 1; + if (item.token === i) { + pos += openQuote.length - 1; + } + text = token.content; + max = text.length; + stack.length = j; + continue OUTER; + } + } + } + if (canOpen) { + stack.push({ + token: i, + pos: t.index, + single: isSingle, + level: thisLevel + }); + } else if (canClose && isSingle) { + token.content = replaceAt(token.content, t.index, APOSTROPHE); + } + } + } +} +function smartquotes(state) { + /* eslint max-depth:0 */ + if (!state.md.options.typographer) { + return; + } + for (let blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { + if (state.tokens[blkIdx].type !== 'inline' || !QUOTE_TEST_RE.test(state.tokens[blkIdx].content)) { + continue; + } + process_inlines(state.tokens[blkIdx].children, state); + } +} + +// Join raw text tokens with the rest of the text +// +// This is set as a separate rule to provide an opportunity for plugins +// to run text replacements after text join, but before escape join. +// +// For example, `\:)` shouldn't be replaced with an emoji. +// + +function text_join(state) { + let curr, last; + const blockTokens = state.tokens; + const l = blockTokens.length; + for (let j = 0; j < l; j++) { + if (blockTokens[j].type !== 'inline') continue; + const tokens = blockTokens[j].children; + const max = tokens.length; + for (curr = 0; curr < max; curr++) { + if (tokens[curr].type === 'text_special') { + tokens[curr].type = 'text'; + } + } + for (curr = last = 0; curr < max; curr++) { + if (tokens[curr].type === 'text' && curr + 1 < max && tokens[curr + 1].type === 'text') { + // collapse two adjacent text nodes + tokens[curr + 1].content = tokens[curr].content + tokens[curr + 1].content; + } else { + if (curr !== last) { + tokens[last] = tokens[curr]; + } + last++; + } + } + if (curr !== last) { + tokens.length = last; + } + } +} + +/** internal + * class Core + * + * Top-level rules executor. Glues block/inline parsers and does intermediate + * transformations. + **/ + +const _rules$2 = [['normalize', normalize], ['block', block], ['inline', inline], ['linkify', linkify$1], ['replacements', replace], ['smartquotes', smartquotes], +// `text_join` finds `text_special` tokens (for escape sequences) +// and joins them with the rest of the text +['text_join', text_join]]; + +/** + * new Core() + **/ +function Core() { + /** + * Core#ruler -> Ruler + * + * [[Ruler]] instance. Keep configuration of core rules. + **/ + this.ruler = new Ruler(); + for (let i = 0; i < _rules$2.length; i++) { + this.ruler.push(_rules$2[i][0], _rules$2[i][1]); + } +} + +/** + * Core.process(state) + * + * Executes core chain rules. + **/ +Core.prototype.process = function (state) { + const rules = this.ruler.getRules(''); + for (let i = 0, l = rules.length; i < l; i++) { + rules[i](state); + } +}; +Core.prototype.State = StateCore; + +// Parser state class + +function StateBlock(src, md, env, tokens) { + this.src = src; + + // link to parser instance + this.md = md; + this.env = env; + + // + // Internal state vartiables + // + + this.tokens = tokens; + this.bMarks = []; // line begin offsets for fast jumps + this.eMarks = []; // line end offsets for fast jumps + this.tShift = []; // offsets of the first non-space characters (tabs not expanded) + this.sCount = []; // indents for each line (tabs expanded) + + // An amount of virtual spaces (tabs expanded) between beginning + // of each line (bMarks) and real beginning of that line. + // + // It exists only as a hack because blockquotes override bMarks + // losing information in the process. + // + // It's used only when expanding tabs, you can think about it as + // an initial tab length, e.g. bsCount=21 applied to string `\t123` + // means first tab should be expanded to 4-21%4 === 3 spaces. + // + this.bsCount = []; + + // block parser variables + + // required block content indent (for example, if we are + // inside a list, it would be positioned after list marker) + this.blkIndent = 0; + this.line = 0; // line index in src + this.lineMax = 0; // lines count + this.tight = false; // loose/tight mode for lists + this.ddIndent = -1; // indent of the current dd block (-1 if there isn't any) + this.listIndent = -1; // indent of the current list block (-1 if there isn't any) + + // can be 'blockquote', 'list', 'root', 'paragraph' or 'reference' + // used in lists to determine if they interrupt a paragraph + this.parentType = 'root'; + this.level = 0; + + // Create caches + // Generate markers. + const s = this.src; + for (let start = 0, pos = 0, indent = 0, offset = 0, len = s.length, indent_found = false; pos < len; pos++) { + const ch = s.charCodeAt(pos); + if (!indent_found) { + if (isSpace(ch)) { + indent++; + if (ch === 0x09) { + offset += 4 - offset % 4; + } else { + offset++; + } + continue; + } else { + indent_found = true; + } + } + if (ch === 0x0A || pos === len - 1) { + if (ch !== 0x0A) { + pos++; + } + this.bMarks.push(start); + this.eMarks.push(pos); + this.tShift.push(indent); + this.sCount.push(offset); + this.bsCount.push(0); + indent_found = false; + indent = 0; + offset = 0; + start = pos + 1; + } + } + + // Push fake entry to simplify cache bounds checks + this.bMarks.push(s.length); + this.eMarks.push(s.length); + this.tShift.push(0); + this.sCount.push(0); + this.bsCount.push(0); + this.lineMax = this.bMarks.length - 1; // don't count last fake line +} + +// Push new token to "stream". +// +StateBlock.prototype.push = function (type, tag, nesting) { + const token = new Token(type, tag, nesting); + token.block = true; + if (nesting < 0) this.level--; // closing tag + token.level = this.level; + if (nesting > 0) this.level++; // opening tag + + this.tokens.push(token); + return token; +}; +StateBlock.prototype.isEmpty = function isEmpty(line) { + return this.bMarks[line] + this.tShift[line] >= this.eMarks[line]; +}; +StateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) { + for (let max = this.lineMax; from < max; from++) { + if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) { + break; + } + } + return from; +}; + +// Skip spaces from given position. +StateBlock.prototype.skipSpaces = function skipSpaces(pos) { + for (let max = this.src.length; pos < max; pos++) { + const ch = this.src.charCodeAt(pos); + if (!isSpace(ch)) { + break; + } + } + return pos; +}; + +// Skip spaces from given position in reverse. +StateBlock.prototype.skipSpacesBack = function skipSpacesBack(pos, min) { + if (pos <= min) { + return pos; + } + while (pos > min) { + if (!isSpace(this.src.charCodeAt(--pos))) { + return pos + 1; + } + } + return pos; +}; + +// Skip char codes from given position +StateBlock.prototype.skipChars = function skipChars(pos, code) { + for (let max = this.src.length; pos < max; pos++) { + if (this.src.charCodeAt(pos) !== code) { + break; + } + } + return pos; +}; + +// Skip char codes reverse from given position - 1 +StateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) { + if (pos <= min) { + return pos; + } + while (pos > min) { + if (code !== this.src.charCodeAt(--pos)) { + return pos + 1; + } + } + return pos; +}; + +// cut lines range from source. +StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) { + if (begin >= end) { + return ''; + } + const queue = new Array(end - begin); + for (let i = 0, line = begin; line < end; line++, i++) { + let lineIndent = 0; + const lineStart = this.bMarks[line]; + let first = lineStart; + let last; + if (line + 1 < end || keepLastLF) { + // No need for bounds check because we have fake entry on tail. + last = this.eMarks[line] + 1; + } else { + last = this.eMarks[line]; + } + while (first < last && lineIndent < indent) { + const ch = this.src.charCodeAt(first); + if (isSpace(ch)) { + if (ch === 0x09) { + lineIndent += 4 - (lineIndent + this.bsCount[line]) % 4; + } else { + lineIndent++; + } + } else if (first - lineStart < this.tShift[line]) { + // patched tShift masked characters to look like spaces (blockquotes, list markers) + lineIndent++; + } else { + break; + } + first++; + } + if (lineIndent > indent) { + // partially expanding tabs in code blocks, e.g '\t\tfoobar' + // with indent=2 becomes ' \tfoobar' + queue[i] = new Array(lineIndent - indent + 1).join(' ') + this.src.slice(first, last); + } else { + queue[i] = this.src.slice(first, last); + } + } + return queue.join(''); +}; + +// re-export Token class to use in block rules +StateBlock.prototype.Token = Token; + +// GFM table, https://github.github.com/gfm/#tables-extension- + +// Limit the amount of empty autocompleted cells in a table, +// see https://github.com/markdown-it/markdown-it/issues/1000, +// +// Both pulldown-cmark and commonmark-hs limit the number of cells this way to ~200k. +// We set it to 65k, which can expand user input by a factor of x370 +// (256x256 square is 1.8kB expanded into 650kB). +const MAX_AUTOCOMPLETED_CELLS = 0x10000; +function getLine(state, line) { + const pos = state.bMarks[line] + state.tShift[line]; + const max = state.eMarks[line]; + return state.src.slice(pos, max); +} +function escapedSplit(str) { + const result = []; + const max = str.length; + let pos = 0; + let ch = str.charCodeAt(pos); + let isEscaped = false; + let lastPos = 0; + let current = ''; + while (pos < max) { + if (ch === 0x7c /* | */) { + if (!isEscaped) { + // pipe separating cells, '|' + result.push(current + str.substring(lastPos, pos)); + current = ''; + lastPos = pos + 1; + } else { + // escaped pipe, '\|' + current += str.substring(lastPos, pos - 1); + lastPos = pos; + } + } + isEscaped = ch === 0x5c /* \ */; + pos++; + ch = str.charCodeAt(pos); + } + result.push(current + str.substring(lastPos)); + return result; +} +function table(state, startLine, endLine, silent) { + // should have at least two lines + if (startLine + 2 > endLine) { + return false; + } + let nextLine = startLine + 1; + if (state.sCount[nextLine] < state.blkIndent) { + return false; + } + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[nextLine] - state.blkIndent >= 4) { + return false; + } + + // first character of the second line should be '|', '-', ':', + // and no other characters are allowed but spaces; + // basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp + + let pos = state.bMarks[nextLine] + state.tShift[nextLine]; + if (pos >= state.eMarks[nextLine]) { + return false; + } + const firstCh = state.src.charCodeAt(pos++); + if (firstCh !== 0x7C /* | */ && firstCh !== 0x2D /* - */ && firstCh !== 0x3A /* : */) { + return false; + } + if (pos >= state.eMarks[nextLine]) { + return false; + } + const secondCh = state.src.charCodeAt(pos++); + if (secondCh !== 0x7C /* | */ && secondCh !== 0x2D /* - */ && secondCh !== 0x3A /* : */ && !isSpace(secondCh)) { + return false; + } + + // if first character is '-', then second character must not be a space + // (due to parsing ambiguity with list) + if (firstCh === 0x2D /* - */ && isSpace(secondCh)) { + return false; + } + while (pos < state.eMarks[nextLine]) { + const ch = state.src.charCodeAt(pos); + if (ch !== 0x7C /* | */ && ch !== 0x2D /* - */ && ch !== 0x3A /* : */ && !isSpace(ch)) { + return false; + } + pos++; + } + let lineText = getLine(state, startLine + 1); + let columns = lineText.split('|'); + const aligns = []; + for (let i = 0; i < columns.length; i++) { + const t = columns[i].trim(); + if (!t) { + // allow empty columns before and after table, but not in between columns; + // e.g. allow ` |---| `, disallow ` ---||--- ` + if (i === 0 || i === columns.length - 1) { + continue; + } else { + return false; + } + } + if (!/^:?-+:?$/.test(t)) { + return false; + } + if (t.charCodeAt(t.length - 1) === 0x3A /* : */) { + aligns.push(t.charCodeAt(0) === 0x3A /* : */ ? 'center' : 'right'); + } else if (t.charCodeAt(0) === 0x3A /* : */) { + aligns.push('left'); + } else { + aligns.push(''); + } + } + lineText = getLine(state, startLine).trim(); + if (lineText.indexOf('|') === -1) { + return false; + } + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false; + } + columns = escapedSplit(lineText); + if (columns.length && columns[0] === '') columns.shift(); + if (columns.length && columns[columns.length - 1] === '') columns.pop(); + + // header row will define an amount of columns in the entire table, + // and align row should be exactly the same (the rest of the rows can differ) + const columnCount = columns.length; + if (columnCount === 0 || columnCount !== aligns.length) { + return false; + } + if (silent) { + return true; + } + const oldParentType = state.parentType; + state.parentType = 'table'; + + // use 'blockquote' lists for termination because it's + // the most similar to tables + const terminatorRules = state.md.block.ruler.getRules('blockquote'); + const token_to = state.push('table_open', 'table', 1); + const tableLines = [startLine, 0]; + token_to.map = tableLines; + const token_tho = state.push('thead_open', 'thead', 1); + token_tho.map = [startLine, startLine + 1]; + const token_htro = state.push('tr_open', 'tr', 1); + token_htro.map = [startLine, startLine + 1]; + for (let i = 0; i < columns.length; i++) { + const token_ho = state.push('th_open', 'th', 1); + if (aligns[i]) { + token_ho.attrs = [['style', 'text-align:' + aligns[i]]]; + } + const token_il = state.push('inline', '', 0); + token_il.content = columns[i].trim(); + token_il.children = []; + state.push('th_close', 'th', -1); + } + state.push('tr_close', 'tr', -1); + state.push('thead_close', 'thead', -1); + let tbodyLines; + let autocompletedCells = 0; + for (nextLine = startLine + 2; nextLine < endLine; nextLine++) { + if (state.sCount[nextLine] < state.blkIndent) { + break; + } + let terminate = false; + for (let i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + if (terminate) { + break; + } + lineText = getLine(state, nextLine).trim(); + if (!lineText) { + break; + } + if (state.sCount[nextLine] - state.blkIndent >= 4) { + break; + } + columns = escapedSplit(lineText); + if (columns.length && columns[0] === '') columns.shift(); + if (columns.length && columns[columns.length - 1] === '') columns.pop(); + + // note: autocomplete count can be negative if user specifies more columns than header, + // but that does not affect intended use (which is limiting expansion) + autocompletedCells += columnCount - columns.length; + if (autocompletedCells > MAX_AUTOCOMPLETED_CELLS) { + break; + } + if (nextLine === startLine + 2) { + const token_tbo = state.push('tbody_open', 'tbody', 1); + token_tbo.map = tbodyLines = [startLine + 2, 0]; + } + const token_tro = state.push('tr_open', 'tr', 1); + token_tro.map = [nextLine, nextLine + 1]; + for (let i = 0; i < columnCount; i++) { + const token_tdo = state.push('td_open', 'td', 1); + if (aligns[i]) { + token_tdo.attrs = [['style', 'text-align:' + aligns[i]]]; + } + const token_il = state.push('inline', '', 0); + token_il.content = columns[i] ? columns[i].trim() : ''; + token_il.children = []; + state.push('td_close', 'td', -1); + } + state.push('tr_close', 'tr', -1); + } + if (tbodyLines) { + state.push('tbody_close', 'tbody', -1); + tbodyLines[1] = nextLine; + } + state.push('table_close', 'table', -1); + tableLines[1] = nextLine; + state.parentType = oldParentType; + state.line = nextLine; + return true; +} + +// Code block (4 spaces padded) + +function code(state, startLine, endLine /*, silent */) { + if (state.sCount[startLine] - state.blkIndent < 4) { + return false; + } + let nextLine = startLine + 1; + let last = nextLine; + while (nextLine < endLine) { + if (state.isEmpty(nextLine)) { + nextLine++; + continue; + } + if (state.sCount[nextLine] - state.blkIndent >= 4) { + nextLine++; + last = nextLine; + continue; + } + break; + } + state.line = last; + const token = state.push('code_block', 'code', 0); + token.content = state.getLines(startLine, last, 4 + state.blkIndent, false) + '\n'; + token.map = [startLine, state.line]; + return true; +} + +// fences (``` lang, ~~~ lang) + +function fence(state, startLine, endLine, silent) { + let pos = state.bMarks[startLine] + state.tShift[startLine]; + let max = state.eMarks[startLine]; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false; + } + if (pos + 3 > max) { + return false; + } + const marker = state.src.charCodeAt(pos); + if (marker !== 0x7E /* ~ */ && marker !== 0x60 /* ` */) { + return false; + } + + // scan marker length + let mem = pos; + pos = state.skipChars(pos, marker); + let len = pos - mem; + if (len < 3) { + return false; + } + const markup = state.src.slice(mem, pos); + const params = state.src.slice(pos, max); + if (marker === 0x60 /* ` */) { + if (params.indexOf(String.fromCharCode(marker)) >= 0) { + return false; + } + } + + // Since start is found, we can report success here in validation mode + if (silent) { + return true; + } + + // search end of block + let nextLine = startLine; + let haveEndMarker = false; + for (;;) { + nextLine++; + if (nextLine >= endLine) { + // unclosed block should be autoclosed by end of document. + // also block seems to be autoclosed by end of parent + break; + } + pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + if (pos < max && state.sCount[nextLine] < state.blkIndent) { + // non-empty line with negative indent should stop the list: + // - ``` + // test + break; + } + if (state.src.charCodeAt(pos) !== marker) { + continue; + } + if (state.sCount[nextLine] - state.blkIndent >= 4) { + // closing fence should be indented less than 4 spaces + continue; + } + pos = state.skipChars(pos, marker); + + // closing code fence must be at least as long as the opening one + if (pos - mem < len) { + continue; + } + + // make sure tail has spaces only + pos = state.skipSpaces(pos); + if (pos < max) { + continue; + } + haveEndMarker = true; + // found! + break; + } + + // If a fence has heading spaces, they should be removed from its inner block + len = state.sCount[startLine]; + state.line = nextLine + (haveEndMarker ? 1 : 0); + const token = state.push('fence', 'code', 0); + token.info = params; + token.content = state.getLines(startLine + 1, nextLine, len, true); + token.markup = markup; + token.map = [startLine, state.line]; + return true; +} + +// Block quotes + +function blockquote(state, startLine, endLine, silent) { + let pos = state.bMarks[startLine] + state.tShift[startLine]; + let max = state.eMarks[startLine]; + const oldLineMax = state.lineMax; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false; + } + + // check the block quote marker + if (state.src.charCodeAt(pos) !== 0x3E /* > */) { + return false; + } + + // we know that it's going to be a valid blockquote, + // so no point trying to find the end of it in silent mode + if (silent) { + return true; + } + const oldBMarks = []; + const oldBSCount = []; + const oldSCount = []; + const oldTShift = []; + const terminatorRules = state.md.block.ruler.getRules('blockquote'); + const oldParentType = state.parentType; + state.parentType = 'blockquote'; + let lastLineEmpty = false; + let nextLine; + + // Search the end of the block + // + // Block ends with either: + // 1. an empty line outside: + // ``` + // > test + // + // ``` + // 2. an empty line inside: + // ``` + // > + // test + // ``` + // 3. another tag: + // ``` + // > test + // - - - + // ``` + for (nextLine = startLine; nextLine < endLine; nextLine++) { + // check if it's outdented, i.e. it's inside list item and indented + // less than said list item: + // + // ``` + // 1. anything + // > current blockquote + // 2. checking this line + // ``` + const isOutdented = state.sCount[nextLine] < state.blkIndent; + pos = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + if (pos >= max) { + // Case 1: line is not inside the blockquote, and this line is empty. + break; + } + if (state.src.charCodeAt(pos++) === 0x3E /* > */ && !isOutdented) { + // This line is inside the blockquote. + + // set offset past spaces and ">" + let initial = state.sCount[nextLine] + 1; + let spaceAfterMarker; + let adjustTab; + + // skip one optional space after '>' + if (state.src.charCodeAt(pos) === 0x20 /* space */) { + // ' > test ' + // ^ -- position start of line here: + pos++; + initial++; + adjustTab = false; + spaceAfterMarker = true; + } else if (state.src.charCodeAt(pos) === 0x09 /* tab */) { + spaceAfterMarker = true; + if ((state.bsCount[nextLine] + initial) % 4 === 3) { + // ' >\t test ' + // ^ -- position start of line here (tab has width===1) + pos++; + initial++; + adjustTab = false; + } else { + // ' >\t test ' + // ^ -- position start of line here + shift bsCount slightly + // to make extra space appear + adjustTab = true; + } + } else { + spaceAfterMarker = false; + } + let offset = initial; + oldBMarks.push(state.bMarks[nextLine]); + state.bMarks[nextLine] = pos; + while (pos < max) { + const ch = state.src.charCodeAt(pos); + if (isSpace(ch)) { + if (ch === 0x09) { + offset += 4 - (offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4; + } else { + offset++; + } + } else { + break; + } + pos++; + } + lastLineEmpty = pos >= max; + oldBSCount.push(state.bsCount[nextLine]); + state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0); + oldSCount.push(state.sCount[nextLine]); + state.sCount[nextLine] = offset - initial; + oldTShift.push(state.tShift[nextLine]); + state.tShift[nextLine] = pos - state.bMarks[nextLine]; + continue; + } + + // Case 2: line is not inside the blockquote, and the last line was empty. + if (lastLineEmpty) { + break; + } + + // Case 3: another tag found. + let terminate = false; + for (let i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + if (terminate) { + // Quirk to enforce "hard termination mode" for paragraphs; + // normally if you call `tokenize(state, startLine, nextLine)`, + // paragraphs will look below nextLine for paragraph continuation, + // but if blockquote is terminated by another tag, they shouldn't + state.lineMax = nextLine; + if (state.blkIndent !== 0) { + // state.blkIndent was non-zero, we now set it to zero, + // so we need to re-calculate all offsets to appear as + // if indent wasn't changed + oldBMarks.push(state.bMarks[nextLine]); + oldBSCount.push(state.bsCount[nextLine]); + oldTShift.push(state.tShift[nextLine]); + oldSCount.push(state.sCount[nextLine]); + state.sCount[nextLine] -= state.blkIndent; + } + break; + } + oldBMarks.push(state.bMarks[nextLine]); + oldBSCount.push(state.bsCount[nextLine]); + oldTShift.push(state.tShift[nextLine]); + oldSCount.push(state.sCount[nextLine]); + + // A negative indentation means that this is a paragraph continuation + // + state.sCount[nextLine] = -1; + } + const oldIndent = state.blkIndent; + state.blkIndent = 0; + const token_o = state.push('blockquote_open', 'blockquote', 1); + token_o.markup = '>'; + const lines = [startLine, 0]; + token_o.map = lines; + state.md.block.tokenize(state, startLine, nextLine); + const token_c = state.push('blockquote_close', 'blockquote', -1); + token_c.markup = '>'; + state.lineMax = oldLineMax; + state.parentType = oldParentType; + lines[1] = state.line; + + // Restore original tShift; this might not be necessary since the parser + // has already been here, but just to make sure we can do that. + for (let i = 0; i < oldTShift.length; i++) { + state.bMarks[i + startLine] = oldBMarks[i]; + state.tShift[i + startLine] = oldTShift[i]; + state.sCount[i + startLine] = oldSCount[i]; + state.bsCount[i + startLine] = oldBSCount[i]; + } + state.blkIndent = oldIndent; + return true; +} + +// Horizontal rule + +function hr(state, startLine, endLine, silent) { + const max = state.eMarks[startLine]; + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false; + } + let pos = state.bMarks[startLine] + state.tShift[startLine]; + const marker = state.src.charCodeAt(pos++); + + // Check hr marker + if (marker !== 0x2A /* * */ && marker !== 0x2D /* - */ && marker !== 0x5F /* _ */) { + return false; + } + + // markers can be mixed with spaces, but there should be at least 3 of them + + let cnt = 1; + while (pos < max) { + const ch = state.src.charCodeAt(pos++); + if (ch !== marker && !isSpace(ch)) { + return false; + } + if (ch === marker) { + cnt++; + } + } + if (cnt < 3) { + return false; + } + if (silent) { + return true; + } + state.line = startLine + 1; + const token = state.push('hr', 'hr', 0); + token.map = [startLine, state.line]; + token.markup = Array(cnt + 1).join(String.fromCharCode(marker)); + return true; +} + +// Lists + +// Search `[-+*][\n ]`, returns next pos after marker on success +// or -1 on fail. +function skipBulletListMarker(state, startLine) { + const max = state.eMarks[startLine]; + let pos = state.bMarks[startLine] + state.tShift[startLine]; + const marker = state.src.charCodeAt(pos++); + // Check bullet + if (marker !== 0x2A /* * */ && marker !== 0x2D /* - */ && marker !== 0x2B /* + */) { + return -1; + } + if (pos < max) { + const ch = state.src.charCodeAt(pos); + if (!isSpace(ch)) { + // " -test " - is not a list item + return -1; + } + } + return pos; +} + +// Search `\d+[.)][\n ]`, returns next pos after marker on success +// or -1 on fail. +function skipOrderedListMarker(state, startLine) { + const start = state.bMarks[startLine] + state.tShift[startLine]; + const max = state.eMarks[startLine]; + let pos = start; + + // List marker should have at least 2 chars (digit + dot) + if (pos + 1 >= max) { + return -1; + } + let ch = state.src.charCodeAt(pos++); + if (ch < 0x30 /* 0 */ || ch > 0x39 /* 9 */) { + return -1; + } + for (;;) { + // EOL -> fail + if (pos >= max) { + return -1; + } + ch = state.src.charCodeAt(pos++); + if (ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */) { + // List marker should have no more than 9 digits + // (prevents integer overflow in browsers) + if (pos - start >= 10) { + return -1; + } + continue; + } + + // found valid marker + if (ch === 0x29 /* ) */ || ch === 0x2e /* . */) { + break; + } + return -1; + } + if (pos < max) { + ch = state.src.charCodeAt(pos); + if (!isSpace(ch)) { + // " 1.test " - is not a list item + return -1; + } + } + return pos; +} +function markTightParagraphs(state, idx) { + const level = state.level + 2; + for (let i = idx + 2, l = state.tokens.length - 2; i < l; i++) { + if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') { + state.tokens[i + 2].hidden = true; + state.tokens[i].hidden = true; + i += 2; + } + } +} +function list(state, startLine, endLine, silent) { + let max, pos, start, token; + let nextLine = startLine; + let tight = true; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[nextLine] - state.blkIndent >= 4) { + return false; + } + + // Special case: + // - item 1 + // - item 2 + // - item 3 + // - item 4 + // - this one is a paragraph continuation + if (state.listIndent >= 0 && state.sCount[nextLine] - state.listIndent >= 4 && state.sCount[nextLine] < state.blkIndent) { + return false; + } + let isTerminatingParagraph = false; + + // limit conditions when list can interrupt + // a paragraph (validation mode only) + if (silent && state.parentType === 'paragraph') { + // Next list item should still terminate previous list item; + // + // This code can fail if plugins use blkIndent as well as lists, + // but I hope the spec gets fixed long before that happens. + // + if (state.sCount[nextLine] >= state.blkIndent) { + isTerminatingParagraph = true; + } + } + + // Detect list type and position after marker + let isOrdered; + let markerValue; + let posAfterMarker; + if ((posAfterMarker = skipOrderedListMarker(state, nextLine)) >= 0) { + isOrdered = true; + start = state.bMarks[nextLine] + state.tShift[nextLine]; + markerValue = Number(state.src.slice(start, posAfterMarker - 1)); + + // If we're starting a new ordered list right after + // a paragraph, it should start with 1. + if (isTerminatingParagraph && markerValue !== 1) return false; + } else if ((posAfterMarker = skipBulletListMarker(state, nextLine)) >= 0) { + isOrdered = false; + } else { + return false; + } + + // If we're starting a new unordered list right after + // a paragraph, first line should not be empty. + if (isTerminatingParagraph) { + if (state.skipSpaces(posAfterMarker) >= state.eMarks[nextLine]) return false; + } + + // For validation mode we can terminate immediately + if (silent) { + return true; + } + + // We should terminate list on style change. Remember first one to compare. + const markerCharCode = state.src.charCodeAt(posAfterMarker - 1); + + // Start list + const listTokIdx = state.tokens.length; + if (isOrdered) { + token = state.push('ordered_list_open', 'ol', 1); + if (markerValue !== 1) { + token.attrs = [['start', markerValue]]; + } + } else { + token = state.push('bullet_list_open', 'ul', 1); + } + const listLines = [nextLine, 0]; + token.map = listLines; + token.markup = String.fromCharCode(markerCharCode); + + // + // Iterate list items + // + + let prevEmptyEnd = false; + const terminatorRules = state.md.block.ruler.getRules('list'); + const oldParentType = state.parentType; + state.parentType = 'list'; + while (nextLine < endLine) { + pos = posAfterMarker; + max = state.eMarks[nextLine]; + const initial = state.sCount[nextLine] + posAfterMarker - (state.bMarks[nextLine] + state.tShift[nextLine]); + let offset = initial; + while (pos < max) { + const ch = state.src.charCodeAt(pos); + if (ch === 0x09) { + offset += 4 - (offset + state.bsCount[nextLine]) % 4; + } else if (ch === 0x20) { + offset++; + } else { + break; + } + pos++; + } + const contentStart = pos; + let indentAfterMarker; + if (contentStart >= max) { + // trimming space in "- \n 3" case, indent is 1 here + indentAfterMarker = 1; + } else { + indentAfterMarker = offset - initial; + } + + // If we have more than 4 spaces, the indent is 1 + // (the rest is just indented code block) + if (indentAfterMarker > 4) { + indentAfterMarker = 1; + } + + // " - test" + // ^^^^^ - calculating total length of this thing + const indent = initial + indentAfterMarker; + + // Run subparser & write tokens + token = state.push('list_item_open', 'li', 1); + token.markup = String.fromCharCode(markerCharCode); + const itemLines = [nextLine, 0]; + token.map = itemLines; + if (isOrdered) { + token.info = state.src.slice(start, posAfterMarker - 1); + } + + // change current state, then restore it after parser subcall + const oldTight = state.tight; + const oldTShift = state.tShift[nextLine]; + const oldSCount = state.sCount[nextLine]; + + // - example list + // ^ listIndent position will be here + // ^ blkIndent position will be here + // + const oldListIndent = state.listIndent; + state.listIndent = state.blkIndent; + state.blkIndent = indent; + state.tight = true; + state.tShift[nextLine] = contentStart - state.bMarks[nextLine]; + state.sCount[nextLine] = offset; + if (contentStart >= max && state.isEmpty(nextLine + 1)) { + // workaround for this case + // (list item is empty, list terminates before "foo"): + // ~~~~~~~~ + // - + // + // foo + // ~~~~~~~~ + state.line = Math.min(state.line + 2, endLine); + } else { + state.md.block.tokenize(state, nextLine, endLine, true); + } + + // If any of list item is tight, mark list as tight + if (!state.tight || prevEmptyEnd) { + tight = false; + } + // Item become loose if finish with empty line, + // but we should filter last element, because it means list finish + prevEmptyEnd = state.line - nextLine > 1 && state.isEmpty(state.line - 1); + state.blkIndent = state.listIndent; + state.listIndent = oldListIndent; + state.tShift[nextLine] = oldTShift; + state.sCount[nextLine] = oldSCount; + state.tight = oldTight; + token = state.push('list_item_close', 'li', -1); + token.markup = String.fromCharCode(markerCharCode); + nextLine = state.line; + itemLines[1] = nextLine; + if (nextLine >= endLine) { + break; + } + + // + // Try to check if list is terminated or continued. + // + if (state.sCount[nextLine] < state.blkIndent) { + break; + } + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[nextLine] - state.blkIndent >= 4) { + break; + } + + // fail if terminating block found + let terminate = false; + for (let i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + if (terminate) { + break; + } + + // fail if list has another type + if (isOrdered) { + posAfterMarker = skipOrderedListMarker(state, nextLine); + if (posAfterMarker < 0) { + break; + } + start = state.bMarks[nextLine] + state.tShift[nextLine]; + } else { + posAfterMarker = skipBulletListMarker(state, nextLine); + if (posAfterMarker < 0) { + break; + } + } + if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { + break; + } + } + + // Finalize list + if (isOrdered) { + token = state.push('ordered_list_close', 'ol', -1); + } else { + token = state.push('bullet_list_close', 'ul', -1); + } + token.markup = String.fromCharCode(markerCharCode); + listLines[1] = nextLine; + state.line = nextLine; + state.parentType = oldParentType; + + // mark paragraphs tight if needed + if (tight) { + markTightParagraphs(state, listTokIdx); + } + return true; +} +function reference(state, startLine, _endLine, silent) { + let pos = state.bMarks[startLine] + state.tShift[startLine]; + let max = state.eMarks[startLine]; + let nextLine = startLine + 1; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false; + } + if (state.src.charCodeAt(pos) !== 0x5B /* [ */) { + return false; + } + function getNextLine(nextLine) { + const endLine = state.lineMax; + if (nextLine >= endLine || state.isEmpty(nextLine)) { + // empty line or end of input + return null; + } + let isContinuation = false; + + // this would be a code block normally, but after paragraph + // it's considered a lazy continuation regardless of what's there + if (state.sCount[nextLine] - state.blkIndent > 3) { + isContinuation = true; + } + + // quirk for blockquotes, this line should already be checked by that rule + if (state.sCount[nextLine] < 0) { + isContinuation = true; + } + if (!isContinuation) { + const terminatorRules = state.md.block.ruler.getRules('reference'); + const oldParentType = state.parentType; + state.parentType = 'reference'; + + // Some tags can terminate paragraph without empty line. + let terminate = false; + for (let i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + state.parentType = oldParentType; + if (terminate) { + // terminated by another block + return null; + } + } + const pos = state.bMarks[nextLine] + state.tShift[nextLine]; + const max = state.eMarks[nextLine]; + + // max + 1 explicitly includes the newline + return state.src.slice(pos, max + 1); + } + let str = state.src.slice(pos, max + 1); + max = str.length; + let labelEnd = -1; + for (pos = 1; pos < max; pos++) { + const ch = str.charCodeAt(pos); + if (ch === 0x5B /* [ */) { + return false; + } else if (ch === 0x5D /* ] */) { + labelEnd = pos; + break; + } else if (ch === 0x0A /* \n */) { + const lineContent = getNextLine(nextLine); + if (lineContent !== null) { + str += lineContent; + max = str.length; + nextLine++; + } + } else if (ch === 0x5C /* \ */) { + pos++; + if (pos < max && str.charCodeAt(pos) === 0x0A) { + const lineContent = getNextLine(nextLine); + if (lineContent !== null) { + str += lineContent; + max = str.length; + nextLine++; + } + } + } + } + if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A /* : */) { + return false; + } + + // [label]: destination 'title' + // ^^^ skip optional whitespace here + for (pos = labelEnd + 2; pos < max; pos++) { + const ch = str.charCodeAt(pos); + if (ch === 0x0A) { + const lineContent = getNextLine(nextLine); + if (lineContent !== null) { + str += lineContent; + max = str.length; + nextLine++; + } + } else if (isSpace(ch)) ;else { + break; + } + } + + // [label]: destination 'title' + // ^^^^^^^^^^^ parse this + const destRes = state.md.helpers.parseLinkDestination(str, pos, max); + if (!destRes.ok) { + return false; + } + const href = state.md.normalizeLink(destRes.str); + if (!state.md.validateLink(href)) { + return false; + } + pos = destRes.pos; + + // save cursor state, we could require to rollback later + const destEndPos = pos; + const destEndLineNo = nextLine; + + // [label]: destination 'title' + // ^^^ skipping those spaces + const start = pos; + for (; pos < max; pos++) { + const ch = str.charCodeAt(pos); + if (ch === 0x0A) { + const lineContent = getNextLine(nextLine); + if (lineContent !== null) { + str += lineContent; + max = str.length; + nextLine++; + } + } else if (isSpace(ch)) ;else { + break; + } + } + + // [label]: destination 'title' + // ^^^^^^^ parse this + let titleRes = state.md.helpers.parseLinkTitle(str, pos, max); + while (titleRes.can_continue) { + const lineContent = getNextLine(nextLine); + if (lineContent === null) break; + str += lineContent; + pos = max; + max = str.length; + nextLine++; + titleRes = state.md.helpers.parseLinkTitle(str, pos, max, titleRes); + } + let title; + if (pos < max && start !== pos && titleRes.ok) { + title = titleRes.str; + pos = titleRes.pos; + } else { + title = ''; + pos = destEndPos; + nextLine = destEndLineNo; + } + + // skip trailing spaces until the rest of the line + while (pos < max) { + const ch = str.charCodeAt(pos); + if (!isSpace(ch)) { + break; + } + pos++; + } + if (pos < max && str.charCodeAt(pos) !== 0x0A) { + if (title) { + // garbage at the end of the line after title, + // but it could still be a valid reference if we roll back + title = ''; + pos = destEndPos; + nextLine = destEndLineNo; + while (pos < max) { + const ch = str.charCodeAt(pos); + if (!isSpace(ch)) { + break; + } + pos++; + } + } + } + if (pos < max && str.charCodeAt(pos) !== 0x0A) { + // garbage at the end of the line + return false; + } + const label = normalizeReference(str.slice(1, labelEnd)); + if (!label) { + // CommonMark 0.20 disallows empty labels + return false; + } + + // Reference can not terminate anything. This check is for safety only. + /* istanbul ignore if */ + if (silent) { + return true; + } + if (typeof state.env.references === 'undefined') { + state.env.references = {}; + } + if (typeof state.env.references[label] === 'undefined') { + state.env.references[label] = { + title, + href + }; + } + state.line = nextLine; + return true; +} + +// List of valid html blocks names, according to commonmark spec +// https://spec.commonmark.org/0.30/#html-blocks + +var block_names = ['address', 'article', 'aside', 'base', 'basefont', 'blockquote', 'body', 'caption', 'center', 'col', 'colgroup', 'dd', 'details', 'dialog', 'dir', 'div', 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'iframe', 'legend', 'li', 'link', 'main', 'menu', 'menuitem', 'nav', 'noframes', 'ol', 'optgroup', 'option', 'p', 'param', 'search', 'section', 'summary', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'track', 'ul']; + +// Regexps to match html elements + +const attr_name = '[a-zA-Z_:][a-zA-Z0-9:._-]*'; +const unquoted = '[^"\'=<>`\\x00-\\x20]+'; +const single_quoted = "'[^']*'"; +const double_quoted = '"[^"]*"'; +const attr_value = '(?:' + unquoted + '|' + single_quoted + '|' + double_quoted + ')'; +const attribute = '(?:\\s+' + attr_name + '(?:\\s*=\\s*' + attr_value + ')?)'; +const open_tag = '<[A-Za-z][A-Za-z0-9\\-]*' + attribute + '*\\s*\\/?>'; +const close_tag = '<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>'; +const comment = ''; +const processing = '<[?][\\s\\S]*?[?]>'; +const declaration = ']*>'; +const cdata = ''; +const HTML_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + '|' + comment + '|' + processing + '|' + declaration + '|' + cdata + ')'); +const HTML_OPEN_CLOSE_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + ')'); + +// HTML block + +// An array of opening and corresponding closing sequences for html tags, +// last argument defines whether it can terminate a paragraph or not +// +const HTML_SEQUENCES = [[/^<(script|pre|style|textarea)(?=(\s|>|$))/i, /<\/(script|pre|style|textarea)>/i, true], [/^/, true], [/^<\?/, /\?>/, true], [/^/, true], [/^/, true], [new RegExp('^|$))', 'i'), /^$/, true], [new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false]]; +function html_block(state, startLine, endLine, silent) { + let pos = state.bMarks[startLine] + state.tShift[startLine]; + let max = state.eMarks[startLine]; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false; + } + if (!state.md.options.html) { + return false; + } + if (state.src.charCodeAt(pos) !== 0x3C /* < */) { + return false; + } + let lineText = state.src.slice(pos, max); + let i = 0; + for (; i < HTML_SEQUENCES.length; i++) { + if (HTML_SEQUENCES[i][0].test(lineText)) { + break; + } + } + if (i === HTML_SEQUENCES.length) { + return false; + } + if (silent) { + // true if this sequence can be a terminator, false otherwise + return HTML_SEQUENCES[i][2]; + } + let nextLine = startLine + 1; + + // If we are here - we detected HTML block. + // Let's roll down till block end. + if (!HTML_SEQUENCES[i][1].test(lineText)) { + for (; nextLine < endLine; nextLine++) { + if (state.sCount[nextLine] < state.blkIndent) { + break; + } + pos = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + lineText = state.src.slice(pos, max); + if (HTML_SEQUENCES[i][1].test(lineText)) { + if (lineText.length !== 0) { + nextLine++; + } + break; + } + } + } + state.line = nextLine; + const token = state.push('html_block', '', 0); + token.map = [startLine, nextLine]; + token.content = state.getLines(startLine, nextLine, state.blkIndent, true); + return true; +} + +// heading (#, ##, ...) + +function heading(state, startLine, endLine, silent) { + let pos = state.bMarks[startLine] + state.tShift[startLine]; + let max = state.eMarks[startLine]; + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false; + } + let ch = state.src.charCodeAt(pos); + if (ch !== 0x23 /* # */ || pos >= max) { + return false; + } + + // count heading level + let level = 1; + ch = state.src.charCodeAt(++pos); + while (ch === 0x23 /* # */ && pos < max && level <= 6) { + level++; + ch = state.src.charCodeAt(++pos); + } + if (level > 6 || pos < max && !isSpace(ch)) { + return false; + } + if (silent) { + return true; + } + + // Let's cut tails like ' ### ' from the end of string + + max = state.skipSpacesBack(max, pos); + const tmp = state.skipCharsBack(max, 0x23, pos); // # + if (tmp > pos && isSpace(state.src.charCodeAt(tmp - 1))) { + max = tmp; + } + state.line = startLine + 1; + const token_o = state.push('heading_open', 'h' + String(level), 1); + token_o.markup = '########'.slice(0, level); + token_o.map = [startLine, state.line]; + const token_i = state.push('inline', '', 0); + token_i.content = state.src.slice(pos, max).trim(); + token_i.map = [startLine, state.line]; + token_i.children = []; + const token_c = state.push('heading_close', 'h' + String(level), -1); + token_c.markup = '########'.slice(0, level); + return true; +} + +// lheading (---, ===) + +function lheading(state, startLine, endLine /*, silent */) { + const terminatorRules = state.md.block.ruler.getRules('paragraph'); + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false; + } + const oldParentType = state.parentType; + state.parentType = 'paragraph'; // use paragraph to match terminatorRules + + // jump line-by-line until empty one or EOF + let level = 0; + let marker; + let nextLine = startLine + 1; + for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { + // this would be a code block normally, but after paragraph + // it's considered a lazy continuation regardless of what's there + if (state.sCount[nextLine] - state.blkIndent > 3) { + continue; + } + + // + // Check for underline in setext header + // + if (state.sCount[nextLine] >= state.blkIndent) { + let pos = state.bMarks[nextLine] + state.tShift[nextLine]; + const max = state.eMarks[nextLine]; + if (pos < max) { + marker = state.src.charCodeAt(pos); + if (marker === 0x2D /* - */ || marker === 0x3D /* = */) { + pos = state.skipChars(pos, marker); + pos = state.skipSpaces(pos); + if (pos >= max) { + level = marker === 0x3D /* = */ ? 1 : 2; + break; + } + } + } + } + + // quirk for blockquotes, this line should already be checked by that rule + if (state.sCount[nextLine] < 0) { + continue; + } + + // Some tags can terminate paragraph without empty line. + let terminate = false; + for (let i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + if (terminate) { + break; + } + } + if (!level) { + // Didn't find valid underline + return false; + } + const content = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); + state.line = nextLine + 1; + const token_o = state.push('heading_open', 'h' + String(level), 1); + token_o.markup = String.fromCharCode(marker); + token_o.map = [startLine, state.line]; + const token_i = state.push('inline', '', 0); + token_i.content = content; + token_i.map = [startLine, state.line - 1]; + token_i.children = []; + const token_c = state.push('heading_close', 'h' + String(level), -1); + token_c.markup = String.fromCharCode(marker); + state.parentType = oldParentType; + return true; +} + +// Paragraph + +function paragraph(state, startLine, endLine) { + const terminatorRules = state.md.block.ruler.getRules('paragraph'); + const oldParentType = state.parentType; + let nextLine = startLine + 1; + state.parentType = 'paragraph'; + + // jump line-by-line until empty one or EOF + for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { + // this would be a code block normally, but after paragraph + // it's considered a lazy continuation regardless of what's there + if (state.sCount[nextLine] - state.blkIndent > 3) { + continue; + } + + // quirk for blockquotes, this line should already be checked by that rule + if (state.sCount[nextLine] < 0) { + continue; + } + + // Some tags can terminate paragraph without empty line. + let terminate = false; + for (let i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true; + break; + } + } + if (terminate) { + break; + } + } + const content = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); + state.line = nextLine; + const token_o = state.push('paragraph_open', 'p', 1); + token_o.map = [startLine, state.line]; + const token_i = state.push('inline', '', 0); + token_i.content = content; + token_i.map = [startLine, state.line]; + token_i.children = []; + state.push('paragraph_close', 'p', -1); + state.parentType = oldParentType; + return true; +} + +/** internal + * class ParserBlock + * + * Block-level tokenizer. + **/ + +const _rules$1 = [ +// First 2 params - rule name & source. Secondary array - list of rules, +// which can be terminated by this one. +['table', table, ['paragraph', 'reference']], ['code', code], ['fence', fence, ['paragraph', 'reference', 'blockquote', 'list']], ['blockquote', blockquote, ['paragraph', 'reference', 'blockquote', 'list']], ['hr', hr, ['paragraph', 'reference', 'blockquote', 'list']], ['list', list, ['paragraph', 'reference', 'blockquote']], ['reference', reference], ['html_block', html_block, ['paragraph', 'reference', 'blockquote']], ['heading', heading, ['paragraph', 'reference', 'blockquote']], ['lheading', lheading], ['paragraph', paragraph]]; + +/** + * new ParserBlock() + **/ +function ParserBlock() { + /** + * ParserBlock#ruler -> Ruler + * + * [[Ruler]] instance. Keep configuration of block rules. + **/ + this.ruler = new Ruler(); + for (let i = 0; i < _rules$1.length; i++) { + this.ruler.push(_rules$1[i][0], _rules$1[i][1], { + alt: (_rules$1[i][2] || []).slice() + }); + } +} + +// Generate tokens for input range +// +ParserBlock.prototype.tokenize = function (state, startLine, endLine) { + const rules = this.ruler.getRules(''); + const len = rules.length; + const maxNesting = state.md.options.maxNesting; + let line = startLine; + let hasEmptyLines = false; + while (line < endLine) { + state.line = line = state.skipEmptyLines(line); + if (line >= endLine) { + break; + } + + // Termination condition for nested calls. + // Nested calls currently used for blockquotes & lists + if (state.sCount[line] < state.blkIndent) { + break; + } + + // If nesting level exceeded - skip tail to the end. That's not ordinary + // situation and we should not care about content. + if (state.level >= maxNesting) { + state.line = endLine; + break; + } + + // Try all possible rules. + // On success, rule should: + // + // - update `state.line` + // - update `state.tokens` + // - return true + const prevLine = state.line; + let ok = false; + for (let i = 0; i < len; i++) { + ok = rules[i](state, line, endLine, false); + if (ok) { + if (prevLine >= state.line) { + throw new Error("block rule didn't increment state.line"); + } + break; + } + } + + // this can only happen if user disables paragraph rule + if (!ok) throw new Error('none of the block rules matched'); + + // set state.tight if we had an empty line before current tag + // i.e. latest empty line should not count + state.tight = !hasEmptyLines; + + // paragraph might "eat" one newline after it in nested lists + if (state.isEmpty(state.line - 1)) { + hasEmptyLines = true; + } + line = state.line; + if (line < endLine && state.isEmpty(line)) { + hasEmptyLines = true; + line++; + state.line = line; + } + } +}; + +/** + * ParserBlock.parse(str, md, env, outTokens) + * + * Process input string and push block tokens into `outTokens` + **/ +ParserBlock.prototype.parse = function (src, md, env, outTokens) { + if (!src) { + return; + } + const state = new this.State(src, md, env, outTokens); + this.tokenize(state, state.line, state.lineMax); +}; +ParserBlock.prototype.State = StateBlock; + +// Inline parser state + +function StateInline(src, md, env, outTokens) { + this.src = src; + this.env = env; + this.md = md; + this.tokens = outTokens; + this.tokens_meta = Array(outTokens.length); + this.pos = 0; + this.posMax = this.src.length; + this.level = 0; + this.pending = ''; + this.pendingLevel = 0; + + // Stores { start: end } pairs. Useful for backtrack + // optimization of pairs parse (emphasis, strikes). + this.cache = {}; + + // List of emphasis-like delimiters for current tag + this.delimiters = []; + + // Stack of delimiter lists for upper level tags + this._prev_delimiters = []; + + // backtick length => last seen position + this.backticks = {}; + this.backticksScanned = false; + + // Counter used to disable inline linkify-it execution + // inside and markdown links + this.linkLevel = 0; +} + +// Flush pending text +// +StateInline.prototype.pushPending = function () { + const token = new Token('text', '', 0); + token.content = this.pending; + token.level = this.pendingLevel; + this.tokens.push(token); + this.pending = ''; + return token; +}; + +// Push new token to "stream". +// If pending text exists - flush it as text token +// +StateInline.prototype.push = function (type, tag, nesting) { + if (this.pending) { + this.pushPending(); + } + const token = new Token(type, tag, nesting); + let token_meta = null; + if (nesting < 0) { + // closing tag + this.level--; + this.delimiters = this._prev_delimiters.pop(); + } + token.level = this.level; + if (nesting > 0) { + // opening tag + this.level++; + this._prev_delimiters.push(this.delimiters); + this.delimiters = []; + token_meta = { + delimiters: this.delimiters + }; + } + this.pendingLevel = this.level; + this.tokens.push(token); + this.tokens_meta.push(token_meta); + return token; +}; + +// Scan a sequence of emphasis-like markers, and determine whether +// it can start an emphasis sequence or end an emphasis sequence. +// +// - start - position to scan from (it should point at a valid marker); +// - canSplitWord - determine if these markers can be found inside a word +// +StateInline.prototype.scanDelims = function (start, canSplitWord) { + const max = this.posMax; + const marker = this.src.charCodeAt(start); + + // treat beginning of the line as a whitespace + const lastChar = start > 0 ? this.src.charCodeAt(start - 1) : 0x20; + let pos = start; + while (pos < max && this.src.charCodeAt(pos) === marker) { + pos++; + } + const count = pos - start; + + // treat end of the line as a whitespace + const nextChar = pos < max ? this.src.charCodeAt(pos) : 0x20; + const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar)); + const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar)); + const isLastWhiteSpace = isWhiteSpace(lastChar); + const isNextWhiteSpace = isWhiteSpace(nextChar); + const left_flanking = !isNextWhiteSpace && (!isNextPunctChar || isLastWhiteSpace || isLastPunctChar); + const right_flanking = !isLastWhiteSpace && (!isLastPunctChar || isNextWhiteSpace || isNextPunctChar); + const can_open = left_flanking && (canSplitWord || !right_flanking || isLastPunctChar); + const can_close = right_flanking && (canSplitWord || !left_flanking || isNextPunctChar); + return { + can_open, + can_close, + length: count + }; +}; + +// re-export Token class to use in block rules +StateInline.prototype.Token = Token; + +// Skip text characters for text token, place those to pending buffer +// and increment current pos + +// Rule to skip pure text +// '{}$%@~+=:' reserved for extentions + +// !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~ + +// !!!! Don't confuse with "Markdown ASCII Punctuation" chars +// http://spec.commonmark.org/0.15/#ascii-punctuation-character +function isTerminatorChar(ch) { + switch (ch) { + case 0x0A /* \n */: + case 0x21 /* ! */: + case 0x23 /* # */: + case 0x24 /* $ */: + case 0x25 /* % */: + case 0x26 /* & */: + case 0x2A /* * */: + case 0x2B /* + */: + case 0x2D /* - */: + case 0x3A /* : */: + case 0x3C /* < */: + case 0x3D /* = */: + case 0x3E /* > */: + case 0x40 /* @ */: + case 0x5B /* [ */: + case 0x5C /* \ */: + case 0x5D /* ] */: + case 0x5E /* ^ */: + case 0x5F /* _ */: + case 0x60 /* ` */: + case 0x7B /* { */: + case 0x7D /* } */: + case 0x7E /* ~ */: + return true; + default: + return false; + } +} +function text(state, silent) { + let pos = state.pos; + while (pos < state.posMax && !isTerminatorChar(state.src.charCodeAt(pos))) { + pos++; + } + if (pos === state.pos) { + return false; + } + if (!silent) { + state.pending += state.src.slice(state.pos, pos); + } + state.pos = pos; + return true; +} + +// Alternative implementation, for memory. +// +// It costs 10% of performance, but allows extend terminators list, if place it +// to `ParserInline` property. Probably, will switch to it sometime, such +// flexibility required. + +/* +var TERMINATOR_RE = /[\n!#$%&*+\-:<=>@[\\\]^_`{}~]/; + +module.exports = function text(state, silent) { + var pos = state.pos, + idx = state.src.slice(pos).search(TERMINATOR_RE); + + // first char is terminator -> empty text + if (idx === 0) { return false; } + + // no terminator -> text till end of string + if (idx < 0) { + if (!silent) { state.pending += state.src.slice(pos); } + state.pos = state.src.length; + return true; + } + + if (!silent) { state.pending += state.src.slice(pos, pos + idx); } + + state.pos += idx; + + return true; +}; */ + +// Process links like https://example.org/ + +// RFC3986: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) +const SCHEME_RE = /(?:^|[^a-z0-9.+-])([a-z][a-z0-9.+-]*)$/i; +function linkify(state, silent) { + if (!state.md.options.linkify) return false; + if (state.linkLevel > 0) return false; + const pos = state.pos; + const max = state.posMax; + if (pos + 3 > max) return false; + if (state.src.charCodeAt(pos) !== 0x3A /* : */) return false; + if (state.src.charCodeAt(pos + 1) !== 0x2F /* / */) return false; + if (state.src.charCodeAt(pos + 2) !== 0x2F /* / */) return false; + const match = state.pending.match(SCHEME_RE); + if (!match) return false; + const proto = match[1]; + const link = state.md.linkify.matchAtStart(state.src.slice(pos - proto.length)); + if (!link) return false; + let url = link.url; + + // invalid link, but still detected by linkify somehow; + // need to check to prevent infinite loop below + if (url.length <= proto.length) return false; + + // disallow '*' at the end of the link (conflicts with emphasis) + url = url.replace(/\*+$/, ''); + const fullUrl = state.md.normalizeLink(url); + if (!state.md.validateLink(fullUrl)) return false; + if (!silent) { + state.pending = state.pending.slice(0, -proto.length); + const token_o = state.push('link_open', 'a', 1); + token_o.attrs = [['href', fullUrl]]; + token_o.markup = 'linkify'; + token_o.info = 'auto'; + const token_t = state.push('text', '', 0); + token_t.content = state.md.normalizeLinkText(url); + const token_c = state.push('link_close', 'a', -1); + token_c.markup = 'linkify'; + token_c.info = 'auto'; + } + state.pos += url.length - proto.length; + return true; +} + +// Proceess '\n' + +function newline(state, silent) { + let pos = state.pos; + if (state.src.charCodeAt(pos) !== 0x0A /* \n */) { + return false; + } + const pmax = state.pending.length - 1; + const max = state.posMax; + + // ' \n' -> hardbreak + // Lookup in pending chars is bad practice! Don't copy to other rules! + // Pending string is stored in concat mode, indexed lookups will cause + // convertion to flat mode. + if (!silent) { + if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) { + if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) { + // Find whitespaces tail of pending chars. + let ws = pmax - 1; + while (ws >= 1 && state.pending.charCodeAt(ws - 1) === 0x20) ws--; + state.pending = state.pending.slice(0, ws); + state.push('hardbreak', 'br', 0); + } else { + state.pending = state.pending.slice(0, -1); + state.push('softbreak', 'br', 0); + } + } else { + state.push('softbreak', 'br', 0); + } + } + pos++; + + // skip heading spaces for next line + while (pos < max && isSpace(state.src.charCodeAt(pos))) { + pos++; + } + state.pos = pos; + return true; +} + +// Process escaped chars and hardbreaks + +const ESCAPED = []; +for (let i = 0; i < 256; i++) { + ESCAPED.push(0); +} +'\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-'.split('').forEach(function (ch) { + ESCAPED[ch.charCodeAt(0)] = 1; +}); +function escape(state, silent) { + let pos = state.pos; + const max = state.posMax; + if (state.src.charCodeAt(pos) !== 0x5C /* \ */) return false; + pos++; + + // '\' at the end of the inline block + if (pos >= max) return false; + let ch1 = state.src.charCodeAt(pos); + if (ch1 === 0x0A) { + if (!silent) { + state.push('hardbreak', 'br', 0); + } + pos++; + // skip leading whitespaces from next line + while (pos < max) { + ch1 = state.src.charCodeAt(pos); + if (!isSpace(ch1)) break; + pos++; + } + state.pos = pos; + return true; + } + let escapedStr = state.src[pos]; + if (ch1 >= 0xD800 && ch1 <= 0xDBFF && pos + 1 < max) { + const ch2 = state.src.charCodeAt(pos + 1); + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + escapedStr += state.src[pos + 1]; + pos++; + } + } + const origStr = '\\' + escapedStr; + if (!silent) { + const token = state.push('text_special', '', 0); + if (ch1 < 256 && ESCAPED[ch1] !== 0) { + token.content = escapedStr; + } else { + token.content = origStr; + } + token.markup = origStr; + token.info = 'escape'; + } + state.pos = pos + 1; + return true; +} + +// Parse backticks + +function backtick(state, silent) { + let pos = state.pos; + const ch = state.src.charCodeAt(pos); + if (ch !== 0x60 /* ` */) { + return false; + } + const start = pos; + pos++; + const max = state.posMax; + + // scan marker length + while (pos < max && state.src.charCodeAt(pos) === 0x60 /* ` */) { + pos++; + } + const marker = state.src.slice(start, pos); + const openerLength = marker.length; + if (state.backticksScanned && (state.backticks[openerLength] || 0) <= start) { + if (!silent) state.pending += marker; + state.pos += openerLength; + return true; + } + let matchEnd = pos; + let matchStart; + + // Nothing found in the cache, scan until the end of the line (or until marker is found) + while ((matchStart = state.src.indexOf('`', matchEnd)) !== -1) { + matchEnd = matchStart + 1; + + // scan marker length + while (matchEnd < max && state.src.charCodeAt(matchEnd) === 0x60 /* ` */) { + matchEnd++; + } + const closerLength = matchEnd - matchStart; + if (closerLength === openerLength) { + // Found matching closer length. + if (!silent) { + const token = state.push('code_inline', 'code', 0); + token.markup = marker; + token.content = state.src.slice(pos, matchStart).replace(/\n/g, ' ').replace(/^ (.+) $/, '$1'); + } + state.pos = matchEnd; + return true; + } + + // Some different length found, put it in cache as upper limit of where closer can be found + state.backticks[closerLength] = matchStart; + } + + // Scanned through the end, didn't find anything + state.backticksScanned = true; + if (!silent) state.pending += marker; + state.pos += openerLength; + return true; +} + +// ~~strike through~~ +// + +// Insert each marker as a separate text token, and add it to delimiter list +// +function strikethrough_tokenize(state, silent) { + const start = state.pos; + const marker = state.src.charCodeAt(start); + if (silent) { + return false; + } + if (marker !== 0x7E /* ~ */) { + return false; + } + const scanned = state.scanDelims(state.pos, true); + let len = scanned.length; + const ch = String.fromCharCode(marker); + if (len < 2) { + return false; + } + let token; + if (len % 2) { + token = state.push('text', '', 0); + token.content = ch; + len--; + } + for (let i = 0; i < len; i += 2) { + token = state.push('text', '', 0); + token.content = ch + ch; + state.delimiters.push({ + marker, + length: 0, + // disable "rule of 3" length checks meant for emphasis + token: state.tokens.length - 1, + end: -1, + open: scanned.can_open, + close: scanned.can_close + }); + } + state.pos += scanned.length; + return true; +} +function postProcess$1(state, delimiters) { + let token; + const loneMarkers = []; + const max = delimiters.length; + for (let i = 0; i < max; i++) { + const startDelim = delimiters[i]; + if (startDelim.marker !== 0x7E /* ~ */) { + continue; + } + if (startDelim.end === -1) { + continue; + } + const endDelim = delimiters[startDelim.end]; + token = state.tokens[startDelim.token]; + token.type = 's_open'; + token.tag = 's'; + token.nesting = 1; + token.markup = '~~'; + token.content = ''; + token = state.tokens[endDelim.token]; + token.type = 's_close'; + token.tag = 's'; + token.nesting = -1; + token.markup = '~~'; + token.content = ''; + if (state.tokens[endDelim.token - 1].type === 'text' && state.tokens[endDelim.token - 1].content === '~') { + loneMarkers.push(endDelim.token - 1); + } + } + + // If a marker sequence has an odd number of characters, it's splitted + // like this: `~~~~~` -> `~` + `~~` + `~~`, leaving one marker at the + // start of the sequence. + // + // So, we have to move all those markers after subsequent s_close tags. + // + while (loneMarkers.length) { + const i = loneMarkers.pop(); + let j = i + 1; + while (j < state.tokens.length && state.tokens[j].type === 's_close') { + j++; + } + j--; + if (i !== j) { + token = state.tokens[j]; + state.tokens[j] = state.tokens[i]; + state.tokens[i] = token; + } + } +} + +// Walk through delimiter list and replace text tokens with tags +// +function strikethrough_postProcess(state) { + const tokens_meta = state.tokens_meta; + const max = state.tokens_meta.length; + postProcess$1(state, state.delimiters); + for (let curr = 0; curr < max; curr++) { + if (tokens_meta[curr] && tokens_meta[curr].delimiters) { + postProcess$1(state, tokens_meta[curr].delimiters); + } + } +} +var r_strikethrough = { + tokenize: strikethrough_tokenize, + postProcess: strikethrough_postProcess +}; + +// Process *this* and _that_ +// + +// Insert each marker as a separate text token, and add it to delimiter list +// +function emphasis_tokenize(state, silent) { + const start = state.pos; + const marker = state.src.charCodeAt(start); + if (silent) { + return false; + } + if (marker !== 0x5F /* _ */ && marker !== 0x2A /* * */) { + return false; + } + const scanned = state.scanDelims(state.pos, marker === 0x2A); + for (let i = 0; i < scanned.length; i++) { + const token = state.push('text', '', 0); + token.content = String.fromCharCode(marker); + state.delimiters.push({ + // Char code of the starting marker (number). + // + marker, + // Total length of these series of delimiters. + // + length: scanned.length, + // A position of the token this delimiter corresponds to. + // + token: state.tokens.length - 1, + // If this delimiter is matched as a valid opener, `end` will be + // equal to its position, otherwise it's `-1`. + // + end: -1, + // Boolean flags that determine if this delimiter could open or close + // an emphasis. + // + open: scanned.can_open, + close: scanned.can_close + }); + } + state.pos += scanned.length; + return true; +} +function postProcess(state, delimiters) { + const max = delimiters.length; + for (let i = max - 1; i >= 0; i--) { + const startDelim = delimiters[i]; + if (startDelim.marker !== 0x5F /* _ */ && startDelim.marker !== 0x2A /* * */) { + continue; + } + + // Process only opening markers + if (startDelim.end === -1) { + continue; + } + const endDelim = delimiters[startDelim.end]; + + // If the previous delimiter has the same marker and is adjacent to this one, + // merge those into one strong delimiter. + // + // `whatever` -> `whatever` + // + const isStrong = i > 0 && delimiters[i - 1].end === startDelim.end + 1 && + // check that first two markers match and adjacent + delimiters[i - 1].marker === startDelim.marker && delimiters[i - 1].token === startDelim.token - 1 && + // check that last two markers are adjacent (we can safely assume they match) + delimiters[startDelim.end + 1].token === endDelim.token + 1; + const ch = String.fromCharCode(startDelim.marker); + const token_o = state.tokens[startDelim.token]; + token_o.type = isStrong ? 'strong_open' : 'em_open'; + token_o.tag = isStrong ? 'strong' : 'em'; + token_o.nesting = 1; + token_o.markup = isStrong ? ch + ch : ch; + token_o.content = ''; + const token_c = state.tokens[endDelim.token]; + token_c.type = isStrong ? 'strong_close' : 'em_close'; + token_c.tag = isStrong ? 'strong' : 'em'; + token_c.nesting = -1; + token_c.markup = isStrong ? ch + ch : ch; + token_c.content = ''; + if (isStrong) { + state.tokens[delimiters[i - 1].token].content = ''; + state.tokens[delimiters[startDelim.end + 1].token].content = ''; + i--; + } + } +} + +// Walk through delimiter list and replace text tokens with tags +// +function emphasis_post_process(state) { + const tokens_meta = state.tokens_meta; + const max = state.tokens_meta.length; + postProcess(state, state.delimiters); + for (let curr = 0; curr < max; curr++) { + if (tokens_meta[curr] && tokens_meta[curr].delimiters) { + postProcess(state, tokens_meta[curr].delimiters); + } + } +} +var r_emphasis = { + tokenize: emphasis_tokenize, + postProcess: emphasis_post_process +}; + +// Process [link]( "stuff") + +function link(state, silent) { + let code, label, res, ref; + let href = ''; + let title = ''; + let start = state.pos; + let parseReference = true; + if (state.src.charCodeAt(state.pos) !== 0x5B /* [ */) { + return false; + } + const oldPos = state.pos; + const max = state.posMax; + const labelStart = state.pos + 1; + const labelEnd = state.md.helpers.parseLinkLabel(state, state.pos, true); + + // parser failed to find ']', so it's not a valid link + if (labelEnd < 0) { + return false; + } + let pos = labelEnd + 1; + if (pos < max && state.src.charCodeAt(pos) === 0x28 /* ( */) { + // + // Inline link + // + + // might have found a valid shortcut link, disable reference parsing + parseReference = false; + + // [link]( "title" ) + // ^^ skipping these spaces + pos++; + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + if (!isSpace(code) && code !== 0x0A) { + break; + } + } + if (pos >= max) { + return false; + } + + // [link]( "title" ) + // ^^^^^^ parsing link destination + start = pos; + res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax); + if (res.ok) { + href = state.md.normalizeLink(res.str); + if (state.md.validateLink(href)) { + pos = res.pos; + } else { + href = ''; + } + + // [link]( "title" ) + // ^^ skipping these spaces + start = pos; + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + if (!isSpace(code) && code !== 0x0A) { + break; + } + } + + // [link]( "title" ) + // ^^^^^^^ parsing link title + res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax); + if (pos < max && start !== pos && res.ok) { + title = res.str; + pos = res.pos; + + // [link]( "title" ) + // ^^ skipping these spaces + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + if (!isSpace(code) && code !== 0x0A) { + break; + } + } + } + } + if (pos >= max || state.src.charCodeAt(pos) !== 0x29 /* ) */) { + // parsing a valid shortcut link failed, fallback to reference + parseReference = true; + } + pos++; + } + if (parseReference) { + // + // Link reference + // + if (typeof state.env.references === 'undefined') { + return false; + } + if (pos < max && state.src.charCodeAt(pos) === 0x5B /* [ */) { + start = pos + 1; + pos = state.md.helpers.parseLinkLabel(state, pos); + if (pos >= 0) { + label = state.src.slice(start, pos++); + } else { + pos = labelEnd + 1; + } + } else { + pos = labelEnd + 1; + } + + // covers label === '' and label === undefined + // (collapsed reference link and shortcut reference link respectively) + if (!label) { + label = state.src.slice(labelStart, labelEnd); + } + ref = state.env.references[normalizeReference(label)]; + if (!ref) { + state.pos = oldPos; + return false; + } + href = ref.href; + title = ref.title; + } + + // + // We found the end of the link, and know for a fact it's a valid link; + // so all that's left to do is to call tokenizer. + // + if (!silent) { + state.pos = labelStart; + state.posMax = labelEnd; + const token_o = state.push('link_open', 'a', 1); + const attrs = [['href', href]]; + token_o.attrs = attrs; + if (title) { + attrs.push(['title', title]); + } + state.linkLevel++; + state.md.inline.tokenize(state); + state.linkLevel--; + state.push('link_close', 'a', -1); + } + state.pos = pos; + state.posMax = max; + return true; +} + +// Process ![image]( "title") + +function image(state, silent) { + let code, content, label, pos, ref, res, title, start; + let href = ''; + const oldPos = state.pos; + const max = state.posMax; + if (state.src.charCodeAt(state.pos) !== 0x21 /* ! */) { + return false; + } + if (state.src.charCodeAt(state.pos + 1) !== 0x5B /* [ */) { + return false; + } + const labelStart = state.pos + 2; + const labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false); + + // parser failed to find ']', so it's not a valid link + if (labelEnd < 0) { + return false; + } + pos = labelEnd + 1; + if (pos < max && state.src.charCodeAt(pos) === 0x28 /* ( */) { + // + // Inline link + // + + // [link]( "title" ) + // ^^ skipping these spaces + pos++; + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + if (!isSpace(code) && code !== 0x0A) { + break; + } + } + if (pos >= max) { + return false; + } + + // [link]( "title" ) + // ^^^^^^ parsing link destination + start = pos; + res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax); + if (res.ok) { + href = state.md.normalizeLink(res.str); + if (state.md.validateLink(href)) { + pos = res.pos; + } else { + href = ''; + } + } + + // [link]( "title" ) + // ^^ skipping these spaces + start = pos; + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + if (!isSpace(code) && code !== 0x0A) { + break; + } + } + + // [link]( "title" ) + // ^^^^^^^ parsing link title + res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax); + if (pos < max && start !== pos && res.ok) { + title = res.str; + pos = res.pos; + + // [link]( "title" ) + // ^^ skipping these spaces + for (; pos < max; pos++) { + code = state.src.charCodeAt(pos); + if (!isSpace(code) && code !== 0x0A) { + break; + } + } + } else { + title = ''; + } + if (pos >= max || state.src.charCodeAt(pos) !== 0x29 /* ) */) { + state.pos = oldPos; + return false; + } + pos++; + } else { + // + // Link reference + // + if (typeof state.env.references === 'undefined') { + return false; + } + if (pos < max && state.src.charCodeAt(pos) === 0x5B /* [ */) { + start = pos + 1; + pos = state.md.helpers.parseLinkLabel(state, pos); + if (pos >= 0) { + label = state.src.slice(start, pos++); + } else { + pos = labelEnd + 1; + } + } else { + pos = labelEnd + 1; + } + + // covers label === '' and label === undefined + // (collapsed reference link and shortcut reference link respectively) + if (!label) { + label = state.src.slice(labelStart, labelEnd); + } + ref = state.env.references[normalizeReference(label)]; + if (!ref) { + state.pos = oldPos; + return false; + } + href = ref.href; + title = ref.title; + } + + // + // We found the end of the link, and know for a fact it's a valid link; + // so all that's left to do is to call tokenizer. + // + if (!silent) { + content = state.src.slice(labelStart, labelEnd); + const tokens = []; + state.md.inline.parse(content, state.md, state.env, tokens); + const token = state.push('image', 'img', 0); + const attrs = [['src', href], ['alt', '']]; + token.attrs = attrs; + token.children = tokens; + token.content = content; + if (title) { + attrs.push(['title', title]); + } + } + state.pos = pos; + state.posMax = max; + return true; +} + +// Process autolinks '' + +/* eslint max-len:0 */ +const EMAIL_RE = /^([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)$/; +/* eslint-disable-next-line no-control-regex */ +const AUTOLINK_RE = /^([a-zA-Z][a-zA-Z0-9+.-]{1,31}):([^<>\x00-\x20]*)$/; +function autolink(state, silent) { + let pos = state.pos; + if (state.src.charCodeAt(pos) !== 0x3C /* < */) { + return false; + } + const start = state.pos; + const max = state.posMax; + for (;;) { + if (++pos >= max) return false; + const ch = state.src.charCodeAt(pos); + if (ch === 0x3C /* < */) return false; + if (ch === 0x3E /* > */) break; + } + const url = state.src.slice(start + 1, pos); + if (AUTOLINK_RE.test(url)) { + const fullUrl = state.md.normalizeLink(url); + if (!state.md.validateLink(fullUrl)) { + return false; + } + if (!silent) { + const token_o = state.push('link_open', 'a', 1); + token_o.attrs = [['href', fullUrl]]; + token_o.markup = 'autolink'; + token_o.info = 'auto'; + const token_t = state.push('text', '', 0); + token_t.content = state.md.normalizeLinkText(url); + const token_c = state.push('link_close', 'a', -1); + token_c.markup = 'autolink'; + token_c.info = 'auto'; + } + state.pos += url.length + 2; + return true; + } + if (EMAIL_RE.test(url)) { + const fullUrl = state.md.normalizeLink('mailto:' + url); + if (!state.md.validateLink(fullUrl)) { + return false; + } + if (!silent) { + const token_o = state.push('link_open', 'a', 1); + token_o.attrs = [['href', fullUrl]]; + token_o.markup = 'autolink'; + token_o.info = 'auto'; + const token_t = state.push('text', '', 0); + token_t.content = state.md.normalizeLinkText(url); + const token_c = state.push('link_close', 'a', -1); + token_c.markup = 'autolink'; + token_c.info = 'auto'; + } + state.pos += url.length + 2; + return true; + } + return false; +} + +// Process html tags + +function isLinkOpen(str) { + return /^\s]/i.test(str); +} +function isLinkClose(str) { + return /^<\/a\s*>/i.test(str); +} +function isLetter(ch) { + /* eslint no-bitwise:0 */ + const lc = ch | 0x20; // to lower case + return lc >= 0x61 /* a */ && lc <= 0x7a /* z */; +} + +function html_inline(state, silent) { + if (!state.md.options.html) { + return false; + } + + // Check start + const max = state.posMax; + const pos = state.pos; + if (state.src.charCodeAt(pos) !== 0x3C /* < */ || pos + 2 >= max) { + return false; + } + + // Quick fail on second char + const ch = state.src.charCodeAt(pos + 1); + if (ch !== 0x21 /* ! */ && ch !== 0x3F /* ? */ && ch !== 0x2F /* / */ && !isLetter(ch)) { + return false; + } + const match = state.src.slice(pos).match(HTML_TAG_RE); + if (!match) { + return false; + } + if (!silent) { + const token = state.push('html_inline', '', 0); + token.content = match[0]; + if (isLinkOpen(token.content)) state.linkLevel++; + if (isLinkClose(token.content)) state.linkLevel--; + } + state.pos += match[0].length; + return true; +} + +// Process html entity - {, ¯, ", ... + +const DIGITAL_RE = /^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i; +const NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i; +function entity(state, silent) { + const pos = state.pos; + const max = state.posMax; + if (state.src.charCodeAt(pos) !== 0x26 /* & */) return false; + if (pos + 1 >= max) return false; + const ch = state.src.charCodeAt(pos + 1); + if (ch === 0x23 /* # */) { + const match = state.src.slice(pos).match(DIGITAL_RE); + if (match) { + if (!silent) { + const code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10); + const token = state.push('text_special', '', 0); + token.content = isValidEntityCode(code) ? fromCodePoint(code) : fromCodePoint(0xFFFD); + token.markup = match[0]; + token.info = 'entity'; + } + state.pos += match[0].length; + return true; + } + } else { + const match = state.src.slice(pos).match(NAMED_RE); + if (match) { + const decoded = entities.decodeHTML(match[0]); + if (decoded !== match[0]) { + if (!silent) { + const token = state.push('text_special', '', 0); + token.content = decoded; + token.markup = match[0]; + token.info = 'entity'; + } + state.pos += match[0].length; + return true; + } + } + } + return false; +} + +// For each opening emphasis-like marker find a matching closing one +// + +function processDelimiters(delimiters) { + const openersBottom = {}; + const max = delimiters.length; + if (!max) return; + + // headerIdx is the first delimiter of the current (where closer is) delimiter run + let headerIdx = 0; + let lastTokenIdx = -2; // needs any value lower than -1 + const jumps = []; + for (let closerIdx = 0; closerIdx < max; closerIdx++) { + const closer = delimiters[closerIdx]; + jumps.push(0); + + // markers belong to same delimiter run if: + // - they have adjacent tokens + // - AND markers are the same + // + if (delimiters[headerIdx].marker !== closer.marker || lastTokenIdx !== closer.token - 1) { + headerIdx = closerIdx; + } + lastTokenIdx = closer.token; + + // Length is only used for emphasis-specific "rule of 3", + // if it's not defined (in strikethrough or 3rd party plugins), + // we can default it to 0 to disable those checks. + // + closer.length = closer.length || 0; + if (!closer.close) continue; + + // Previously calculated lower bounds (previous fails) + // for each marker, each delimiter length modulo 3, + // and for whether this closer can be an opener; + // https://github.com/commonmark/cmark/commit/34250e12ccebdc6372b8b49c44fab57c72443460 + /* eslint-disable-next-line no-prototype-builtins */ + if (!openersBottom.hasOwnProperty(closer.marker)) { + openersBottom[closer.marker] = [-1, -1, -1, -1, -1, -1]; + } + const minOpenerIdx = openersBottom[closer.marker][(closer.open ? 3 : 0) + closer.length % 3]; + let openerIdx = headerIdx - jumps[headerIdx] - 1; + let newMinOpenerIdx = openerIdx; + for (; openerIdx > minOpenerIdx; openerIdx -= jumps[openerIdx] + 1) { + const opener = delimiters[openerIdx]; + if (opener.marker !== closer.marker) continue; + if (opener.open && opener.end < 0) { + let isOddMatch = false; + + // from spec: + // + // If one of the delimiters can both open and close emphasis, then the + // sum of the lengths of the delimiter runs containing the opening and + // closing delimiters must not be a multiple of 3 unless both lengths + // are multiples of 3. + // + if (opener.close || closer.open) { + if ((opener.length + closer.length) % 3 === 0) { + if (opener.length % 3 !== 0 || closer.length % 3 !== 0) { + isOddMatch = true; + } + } + } + if (!isOddMatch) { + // If previous delimiter cannot be an opener, we can safely skip + // the entire sequence in future checks. This is required to make + // sure algorithm has linear complexity (see *_*_*_*_*_... case). + // + const lastJump = openerIdx > 0 && !delimiters[openerIdx - 1].open ? jumps[openerIdx - 1] + 1 : 0; + jumps[closerIdx] = closerIdx - openerIdx + lastJump; + jumps[openerIdx] = lastJump; + closer.open = false; + opener.end = closerIdx; + opener.close = false; + newMinOpenerIdx = -1; + // treat next token as start of run, + // it optimizes skips in **<...>**a**<...>** pathological case + lastTokenIdx = -2; + break; + } + } + } + if (newMinOpenerIdx !== -1) { + // If match for this delimiter run failed, we want to set lower bound for + // future lookups. This is required to make sure algorithm has linear + // complexity. + // + // See details here: + // https://github.com/commonmark/cmark/issues/178#issuecomment-270417442 + // + openersBottom[closer.marker][(closer.open ? 3 : 0) + (closer.length || 0) % 3] = newMinOpenerIdx; + } + } +} +function link_pairs(state) { + const tokens_meta = state.tokens_meta; + const max = state.tokens_meta.length; + processDelimiters(state.delimiters); + for (let curr = 0; curr < max; curr++) { + if (tokens_meta[curr] && tokens_meta[curr].delimiters) { + processDelimiters(tokens_meta[curr].delimiters); + } + } +} + +// Clean up tokens after emphasis and strikethrough postprocessing: +// merge adjacent text nodes into one and re-calculate all token levels +// +// This is necessary because initially emphasis delimiter markers (*, _, ~) +// are treated as their own separate text tokens. Then emphasis rule either +// leaves them as text (needed to merge with adjacent text) or turns them +// into opening/closing tags (which messes up levels inside). +// + +function fragments_join(state) { + let curr, last; + let level = 0; + const tokens = state.tokens; + const max = state.tokens.length; + for (curr = last = 0; curr < max; curr++) { + // re-calculate levels after emphasis/strikethrough turns some text nodes + // into opening/closing tags + if (tokens[curr].nesting < 0) level--; // closing tag + tokens[curr].level = level; + if (tokens[curr].nesting > 0) level++; // opening tag + + if (tokens[curr].type === 'text' && curr + 1 < max && tokens[curr + 1].type === 'text') { + // collapse two adjacent text nodes + tokens[curr + 1].content = tokens[curr].content + tokens[curr + 1].content; + } else { + if (curr !== last) { + tokens[last] = tokens[curr]; + } + last++; + } + } + if (curr !== last) { + tokens.length = last; + } +} + +/** internal + * class ParserInline + * + * Tokenizes paragraph content. + **/ + +// Parser rules + +const _rules = [['text', text], ['linkify', linkify], ['newline', newline], ['escape', escape], ['backticks', backtick], ['strikethrough', r_strikethrough.tokenize], ['emphasis', r_emphasis.tokenize], ['link', link], ['image', image], ['autolink', autolink], ['html_inline', html_inline], ['entity', entity]]; + +// `rule2` ruleset was created specifically for emphasis/strikethrough +// post-processing and may be changed in the future. +// +// Don't use this for anything except pairs (plugins working with `balance_pairs`). +// +const _rules2 = [['balance_pairs', link_pairs], ['strikethrough', r_strikethrough.postProcess], ['emphasis', r_emphasis.postProcess], +// rules for pairs separate '**' into its own text tokens, which may be left unused, +// rule below merges unused segments back with the rest of the text +['fragments_join', fragments_join]]; + +/** + * new ParserInline() + **/ +function ParserInline() { + /** + * ParserInline#ruler -> Ruler + * + * [[Ruler]] instance. Keep configuration of inline rules. + **/ + this.ruler = new Ruler(); + for (let i = 0; i < _rules.length; i++) { + this.ruler.push(_rules[i][0], _rules[i][1]); + } + + /** + * ParserInline#ruler2 -> Ruler + * + * [[Ruler]] instance. Second ruler used for post-processing + * (e.g. in emphasis-like rules). + **/ + this.ruler2 = new Ruler(); + for (let i = 0; i < _rules2.length; i++) { + this.ruler2.push(_rules2[i][0], _rules2[i][1]); + } +} + +// Skip single token by running all rules in validation mode; +// returns `true` if any rule reported success +// +ParserInline.prototype.skipToken = function (state) { + const pos = state.pos; + const rules = this.ruler.getRules(''); + const len = rules.length; + const maxNesting = state.md.options.maxNesting; + const cache = state.cache; + if (typeof cache[pos] !== 'undefined') { + state.pos = cache[pos]; + return; + } + let ok = false; + if (state.level < maxNesting) { + for (let i = 0; i < len; i++) { + // Increment state.level and decrement it later to limit recursion. + // It's harmless to do here, because no tokens are created. But ideally, + // we'd need a separate private state variable for this purpose. + // + state.level++; + ok = rules[i](state, true); + state.level--; + if (ok) { + if (pos >= state.pos) { + throw new Error("inline rule didn't increment state.pos"); + } + break; + } + } + } else { + // Too much nesting, just skip until the end of the paragraph. + // + // NOTE: this will cause links to behave incorrectly in the following case, + // when an amount of `[` is exactly equal to `maxNesting + 1`: + // + // [[[[[[[[[[[[[[[[[[[[[foo]() + // + // TODO: remove this workaround when CM standard will allow nested links + // (we can replace it by preventing links from being parsed in + // validation mode) + // + state.pos = state.posMax; + } + if (!ok) { + state.pos++; + } + cache[pos] = state.pos; +}; + +// Generate tokens for input range +// +ParserInline.prototype.tokenize = function (state) { + const rules = this.ruler.getRules(''); + const len = rules.length; + const end = state.posMax; + const maxNesting = state.md.options.maxNesting; + while (state.pos < end) { + // Try all possible rules. + // On success, rule should: + // + // - update `state.pos` + // - update `state.tokens` + // - return true + const prevPos = state.pos; + let ok = false; + if (state.level < maxNesting) { + for (let i = 0; i < len; i++) { + ok = rules[i](state, false); + if (ok) { + if (prevPos >= state.pos) { + throw new Error("inline rule didn't increment state.pos"); + } + break; + } + } + } + if (ok) { + if (state.pos >= end) { + break; + } + continue; + } + state.pending += state.src[state.pos++]; + } + if (state.pending) { + state.pushPending(); + } +}; + +/** + * ParserInline.parse(str, md, env, outTokens) + * + * Process input string and push inline tokens into `outTokens` + **/ +ParserInline.prototype.parse = function (str, md, env, outTokens) { + const state = new this.State(str, md, env, outTokens); + this.tokenize(state); + const rules = this.ruler2.getRules(''); + const len = rules.length; + for (let i = 0; i < len; i++) { + rules[i](state); + } +}; +ParserInline.prototype.State = StateInline; + +// markdown-it default options + +var cfg_default = { + options: { + // Enable HTML tags in source + html: false, + // Use '/' to close single tags (
    ) + xhtmlOut: false, + // Convert '\n' in paragraphs into
    + breaks: false, + // CSS language prefix for fenced blocks + langPrefix: 'language-', + // autoconvert URL-like texts to links + linkify: false, + // Enable some language-neutral replacements + quotes beautification + typographer: false, + // Double + single quotes replacement pairs, when typographer enabled, + // and smartquotes on. Could be either a String or an Array. + // + // For example, you can use '«»„“' for Russian, '„“‚‘' for German, + // and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp). + quotes: '\u201c\u201d\u2018\u2019', + /* “”‘’ */ + + // Highlighter function. Should return escaped HTML, + // or '' if the source string is not changed and should be escaped externaly. + // If result starts with ) + xhtmlOut: false, + // Convert '\n' in paragraphs into
    + breaks: false, + // CSS language prefix for fenced blocks + langPrefix: 'language-', + // autoconvert URL-like texts to links + linkify: false, + // Enable some language-neutral replacements + quotes beautification + typographer: false, + // Double + single quotes replacement pairs, when typographer enabled, + // and smartquotes on. Could be either a String or an Array. + // + // For example, you can use '«»„“' for Russian, '„“‚‘' for German, + // and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp). + quotes: '\u201c\u201d\u2018\u2019', + /* “”‘’ */ + + // Highlighter function. Should return escaped HTML, + // or '' if the source string is not changed and should be escaped externaly. + // If result starts with ) + xhtmlOut: true, + // Convert '\n' in paragraphs into
    + breaks: false, + // CSS language prefix for fenced blocks + langPrefix: 'language-', + // autoconvert URL-like texts to links + linkify: false, + // Enable some language-neutral replacements + quotes beautification + typographer: false, + // Double + single quotes replacement pairs, when typographer enabled, + // and smartquotes on. Could be either a String or an Array. + // + // For example, you can use '«»„“' for Russian, '„“‚‘' for German, + // and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp). + quotes: '\u201c\u201d\u2018\u2019', + /* “”‘’ */ + + // Highlighter function. Should return escaped HTML, + // or '' if the source string is not changed and should be escaped externaly. + // If result starts with = 0) { + try { + parsed.hostname = punycode.toASCII(parsed.hostname); + } catch (er) {/**/} + } + } + return mdurl__namespace.encode(mdurl__namespace.format(parsed)); +} +function normalizeLinkText(url) { + const parsed = mdurl__namespace.parse(url, true); + if (parsed.hostname) { + // Encode hostnames in urls like: + // `http://host/`, `https://host/`, `mailto:user@host`, `//host/` + // + // We don't encode unknown schemas, because it's likely that we encode + // something we shouldn't (e.g. `skype:name` treated as `skype:host`) + // + if (!parsed.protocol || RECODE_HOSTNAME_FOR.indexOf(parsed.protocol) >= 0) { + try { + parsed.hostname = punycode.toUnicode(parsed.hostname); + } catch (er) {/**/} + } + } + + // add '%' to exclude list because of https://github.com/markdown-it/markdown-it/issues/720 + return mdurl__namespace.decode(mdurl__namespace.format(parsed), mdurl__namespace.decode.defaultChars + '%'); +} + +/** + * class MarkdownIt + * + * Main parser/renderer class. + * + * ##### Usage + * + * ```javascript + * // node.js, "classic" way: + * var MarkdownIt = require('markdown-it'), + * md = new MarkdownIt(); + * var result = md.render('# markdown-it rulezz!'); + * + * // node.js, the same, but with sugar: + * var md = require('markdown-it')(); + * var result = md.render('# markdown-it rulezz!'); + * + * // browser without AMD, added to "window" on script load + * // Note, there are no dash. + * var md = window.markdownit(); + * var result = md.render('# markdown-it rulezz!'); + * ``` + * + * Single line rendering, without paragraph wrap: + * + * ```javascript + * var md = require('markdown-it')(); + * var result = md.renderInline('__markdown-it__ rulezz!'); + * ``` + **/ + +/** + * new MarkdownIt([presetName, options]) + * - presetName (String): optional, `commonmark` / `zero` + * - options (Object) + * + * Creates parser instanse with given config. Can be called without `new`. + * + * ##### presetName + * + * MarkdownIt provides named presets as a convenience to quickly + * enable/disable active syntax rules and options for common use cases. + * + * - ["commonmark"](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/commonmark.mjs) - + * configures parser to strict [CommonMark](http://commonmark.org/) mode. + * - [default](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/default.mjs) - + * similar to GFM, used when no preset name given. Enables all available rules, + * but still without html, typographer & autolinker. + * - ["zero"](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/zero.mjs) - + * all rules disabled. Useful to quickly setup your config via `.enable()`. + * For example, when you need only `bold` and `italic` markup and nothing else. + * + * ##### options: + * + * - __html__ - `false`. Set `true` to enable HTML tags in source. Be careful! + * That's not safe! You may need external sanitizer to protect output from XSS. + * It's better to extend features via plugins, instead of enabling HTML. + * - __xhtmlOut__ - `false`. Set `true` to add '/' when closing single tags + * (`
    `). This is needed only for full CommonMark compatibility. In real + * world you will need HTML output. + * - __breaks__ - `false`. Set `true` to convert `\n` in paragraphs into `
    `. + * - __langPrefix__ - `language-`. CSS language class prefix for fenced blocks. + * Can be useful for external highlighters. + * - __linkify__ - `false`. Set `true` to autoconvert URL-like text to links. + * - __typographer__ - `false`. Set `true` to enable [some language-neutral + * replacement](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.mjs) + + * quotes beautification (smartquotes). + * - __quotes__ - `“”‘’`, String or Array. Double + single quotes replacement + * pairs, when typographer enabled and smartquotes on. For example, you can + * use `'«»„“'` for Russian, `'„“‚‘'` for German, and + * `['«\xA0', '\xA0»', '‹\xA0', '\xA0›']` for French (including nbsp). + * - __highlight__ - `null`. Highlighter function for fenced code blocks. + * Highlighter `function (str, lang)` should return escaped HTML. It can also + * return empty string if the source was not changed and should be escaped + * externaly. If result starts with ` or ``): + * + * ```javascript + * var hljs = require('highlight.js') // https://highlightjs.org/ + * + * // Actual default values + * var md = require('markdown-it')({ + * highlight: function (str, lang) { + * if (lang && hljs.getLanguage(lang)) { + * try { + * return '
    ' +
    + *                hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
    + *                '
    '; + * } catch (__) {} + * } + * + * return '
    ' + md.utils.escapeHtml(str) + '
    '; + * } + * }); + * ``` + * + **/ +function MarkdownIt(presetName, options) { + if (!(this instanceof MarkdownIt)) { + return new MarkdownIt(presetName, options); + } + if (!options) { + if (!isString(presetName)) { + options = presetName || {}; + presetName = 'default'; + } + } + + /** + * MarkdownIt#inline -> ParserInline + * + * Instance of [[ParserInline]]. You may need it to add new rules when + * writing plugins. For simple rules control use [[MarkdownIt.disable]] and + * [[MarkdownIt.enable]]. + **/ + this.inline = new ParserInline(); + + /** + * MarkdownIt#block -> ParserBlock + * + * Instance of [[ParserBlock]]. You may need it to add new rules when + * writing plugins. For simple rules control use [[MarkdownIt.disable]] and + * [[MarkdownIt.enable]]. + **/ + this.block = new ParserBlock(); + + /** + * MarkdownIt#core -> Core + * + * Instance of [[Core]] chain executor. You may need it to add new rules when + * writing plugins. For simple rules control use [[MarkdownIt.disable]] and + * [[MarkdownIt.enable]]. + **/ + this.core = new Core(); + + /** + * MarkdownIt#renderer -> Renderer + * + * Instance of [[Renderer]]. Use it to modify output look. Or to add rendering + * rules for new token types, generated by plugins. + * + * ##### Example + * + * ```javascript + * var md = require('markdown-it')(); + * + * function myToken(tokens, idx, options, env, self) { + * //... + * return result; + * }; + * + * md.renderer.rules['my_token'] = myToken + * ``` + * + * See [[Renderer]] docs and [source code](https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.mjs). + **/ + this.renderer = new Renderer(); + + /** + * MarkdownIt#linkify -> LinkifyIt + * + * [linkify-it](https://github.com/markdown-it/linkify-it) instance. + * Used by [linkify](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/linkify.mjs) + * rule. + **/ + this.linkify = new LinkifyIt(); + + /** + * MarkdownIt#validateLink(url) -> Boolean + * + * Link validation function. CommonMark allows too much in links. By default + * we disable `javascript:`, `vbscript:`, `file:` schemas, and almost all `data:...` schemas + * except some embedded image types. + * + * You can change this behaviour: + * + * ```javascript + * var md = require('markdown-it')(); + * // enable everything + * md.validateLink = function () { return true; } + * ``` + **/ + this.validateLink = validateLink; + + /** + * MarkdownIt#normalizeLink(url) -> String + * + * Function used to encode link url to a machine-readable format, + * which includes url-encoding, punycode, etc. + **/ + this.normalizeLink = normalizeLink; + + /** + * MarkdownIt#normalizeLinkText(url) -> String + * + * Function used to decode link url to a human-readable format` + **/ + this.normalizeLinkText = normalizeLinkText; + + // Expose utils & helpers for easy acces from plugins + + /** + * MarkdownIt#utils -> utils + * + * Assorted utility functions, useful to write plugins. See details + * [here](https://github.com/markdown-it/markdown-it/blob/master/lib/common/utils.mjs). + **/ + this.utils = utils; + + /** + * MarkdownIt#helpers -> helpers + * + * Link components parser functions, useful to write plugins. See details + * [here](https://github.com/markdown-it/markdown-it/blob/master/lib/helpers). + **/ + this.helpers = assign({}, helpers); + this.options = {}; + this.configure(presetName); + if (options) { + this.set(options); + } +} + +/** chainable + * MarkdownIt.set(options) + * + * Set parser options (in the same format as in constructor). Probably, you + * will never need it, but you can change options after constructor call. + * + * ##### Example + * + * ```javascript + * var md = require('markdown-it')() + * .set({ html: true, breaks: true }) + * .set({ typographer, true }); + * ``` + * + * __Note:__ To achieve the best possible performance, don't modify a + * `markdown-it` instance options on the fly. If you need multiple configurations + * it's best to create multiple instances and initialize each with separate + * config. + **/ +MarkdownIt.prototype.set = function (options) { + assign(this.options, options); + return this; +}; + +/** chainable, internal + * MarkdownIt.configure(presets) + * + * Batch load of all options and compenent settings. This is internal method, + * and you probably will not need it. But if you will - see available presets + * and data structure [here](https://github.com/markdown-it/markdown-it/tree/master/lib/presets) + * + * We strongly recommend to use presets instead of direct config loads. That + * will give better compatibility with next versions. + **/ +MarkdownIt.prototype.configure = function (presets) { + const self = this; + if (isString(presets)) { + const presetName = presets; + presets = config[presetName]; + if (!presets) { + throw new Error('Wrong `markdown-it` preset "' + presetName + '", check name'); + } + } + if (!presets) { + throw new Error('Wrong `markdown-it` preset, can\'t be empty'); + } + if (presets.options) { + self.set(presets.options); + } + if (presets.components) { + Object.keys(presets.components).forEach(function (name) { + if (presets.components[name].rules) { + self[name].ruler.enableOnly(presets.components[name].rules); + } + if (presets.components[name].rules2) { + self[name].ruler2.enableOnly(presets.components[name].rules2); + } + }); + } + return this; +}; + +/** chainable + * MarkdownIt.enable(list, ignoreInvalid) + * - list (String|Array): rule name or list of rule names to enable + * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. + * + * Enable list or rules. It will automatically find appropriate components, + * containing rules with given names. If rule not found, and `ignoreInvalid` + * not set - throws exception. + * + * ##### Example + * + * ```javascript + * var md = require('markdown-it')() + * .enable(['sub', 'sup']) + * .disable('smartquotes'); + * ``` + **/ +MarkdownIt.prototype.enable = function (list, ignoreInvalid) { + let result = []; + if (!Array.isArray(list)) { + list = [list]; + } + ['core', 'block', 'inline'].forEach(function (chain) { + result = result.concat(this[chain].ruler.enable(list, true)); + }, this); + result = result.concat(this.inline.ruler2.enable(list, true)); + const missed = list.filter(function (name) { + return result.indexOf(name) < 0; + }); + if (missed.length && !ignoreInvalid) { + throw new Error('MarkdownIt. Failed to enable unknown rule(s): ' + missed); + } + return this; +}; + +/** chainable + * MarkdownIt.disable(list, ignoreInvalid) + * - list (String|Array): rule name or list of rule names to disable. + * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. + * + * The same as [[MarkdownIt.enable]], but turn specified rules off. + **/ +MarkdownIt.prototype.disable = function (list, ignoreInvalid) { + let result = []; + if (!Array.isArray(list)) { + list = [list]; + } + ['core', 'block', 'inline'].forEach(function (chain) { + result = result.concat(this[chain].ruler.disable(list, true)); + }, this); + result = result.concat(this.inline.ruler2.disable(list, true)); + const missed = list.filter(function (name) { + return result.indexOf(name) < 0; + }); + if (missed.length && !ignoreInvalid) { + throw new Error('MarkdownIt. Failed to disable unknown rule(s): ' + missed); + } + return this; +}; + +/** chainable + * MarkdownIt.use(plugin, params) + * + * Load specified plugin with given params into current parser instance. + * It's just a sugar to call `plugin(md, params)` with curring. + * + * ##### Example + * + * ```javascript + * var iterator = require('markdown-it-for-inline'); + * var md = require('markdown-it')() + * .use(iterator, 'foo_replace', 'text', function (tokens, idx) { + * tokens[idx].content = tokens[idx].content.replace(/foo/g, 'bar'); + * }); + * ``` + **/ +MarkdownIt.prototype.use = function (plugin /*, params, ... */) { + const args = [this].concat(Array.prototype.slice.call(arguments, 1)); + plugin.apply(plugin, args); + return this; +}; + +/** internal + * MarkdownIt.parse(src, env) -> Array + * - src (String): source string + * - env (Object): environment sandbox + * + * Parse input string and return list of block tokens (special token type + * "inline" will contain list of inline tokens). You should not call this + * method directly, until you write custom renderer (for example, to produce + * AST). + * + * `env` is used to pass data between "distributed" rules and return additional + * metadata like reference info, needed for the renderer. It also can be used to + * inject data in specific cases. Usually, you will be ok to pass `{}`, + * and then pass updated object to renderer. + **/ +MarkdownIt.prototype.parse = function (src, env) { + if (typeof src !== 'string') { + throw new Error('Input data should be a String'); + } + const state = new this.core.State(src, this, env); + this.core.process(state); + return state.tokens; +}; + +/** + * MarkdownIt.render(src [, env]) -> String + * - src (String): source string + * - env (Object): environment sandbox + * + * Render markdown string into html. It does all magic for you :). + * + * `env` can be used to inject additional metadata (`{}` by default). + * But you will not need it with high probability. See also comment + * in [[MarkdownIt.parse]]. + **/ +MarkdownIt.prototype.render = function (src, env) { + env = env || {}; + return this.renderer.render(this.parse(src, env), this.options, env); +}; + +/** internal + * MarkdownIt.parseInline(src, env) -> Array + * - src (String): source string + * - env (Object): environment sandbox + * + * The same as [[MarkdownIt.parse]] but skip all block rules. It returns the + * block tokens list with the single `inline` element, containing parsed inline + * tokens in `children` property. Also updates `env` object. + **/ +MarkdownIt.prototype.parseInline = function (src, env) { + const state = new this.core.State(src, this, env); + state.inlineMode = true; + this.core.process(state); + return state.tokens; +}; + +/** + * MarkdownIt.renderInline(src [, env]) -> String + * - src (String): source string + * - env (Object): environment sandbox + * + * Similar to [[MarkdownIt.render]] but for single paragraph content. Result + * will NOT be wrapped into `

    ` tags. + **/ +MarkdownIt.prototype.renderInline = function (src, env) { + env = env || {}; + return this.renderer.render(this.parseInline(src, env), this.options, env); +}; +module.exports = MarkdownIt; + +/***/ }), + +/***/ "../node_modules/mdurl/build/index.cjs.js": +/*!************************************************!*\ + !*** ../node_modules/mdurl/build/index.cjs.js ***! + \************************************************/ +/***/ (function(__unused_webpack_module, exports) { + + + +/* eslint-disable no-bitwise */ +const decodeCache = {}; +function getDecodeCache(exclude) { + let cache = decodeCache[exclude]; + if (cache) { + return cache; + } + cache = decodeCache[exclude] = []; + for (let i = 0; i < 128; i++) { + const ch = String.fromCharCode(i); + cache.push(ch); + } + for (let i = 0; i < exclude.length; i++) { + const ch = exclude.charCodeAt(i); + cache[ch] = '%' + ('0' + ch.toString(16).toUpperCase()).slice(-2); + } + return cache; +} + +// Decode percent-encoded string. +// +function decode(string, exclude) { + if (typeof exclude !== 'string') { + exclude = decode.defaultChars; + } + const cache = getDecodeCache(exclude); + return string.replace(/(%[a-f0-9]{2})+/gi, function (seq) { + let result = ''; + for (let i = 0, l = seq.length; i < l; i += 3) { + const b1 = parseInt(seq.slice(i + 1, i + 3), 16); + if (b1 < 0x80) { + result += cache[b1]; + continue; + } + if ((b1 & 0xE0) === 0xC0 && i + 3 < l) { + // 110xxxxx 10xxxxxx + const b2 = parseInt(seq.slice(i + 4, i + 6), 16); + if ((b2 & 0xC0) === 0x80) { + const chr = b1 << 6 & 0x7C0 | b2 & 0x3F; + if (chr < 0x80) { + result += '\ufffd\ufffd'; + } else { + result += String.fromCharCode(chr); + } + i += 3; + continue; + } + } + if ((b1 & 0xF0) === 0xE0 && i + 6 < l) { + // 1110xxxx 10xxxxxx 10xxxxxx + const b2 = parseInt(seq.slice(i + 4, i + 6), 16); + const b3 = parseInt(seq.slice(i + 7, i + 9), 16); + if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) { + const chr = b1 << 12 & 0xF000 | b2 << 6 & 0xFC0 | b3 & 0x3F; + if (chr < 0x800 || chr >= 0xD800 && chr <= 0xDFFF) { + result += '\ufffd\ufffd\ufffd'; + } else { + result += String.fromCharCode(chr); + } + i += 6; + continue; + } + } + if ((b1 & 0xF8) === 0xF0 && i + 9 < l) { + // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx + const b2 = parseInt(seq.slice(i + 4, i + 6), 16); + const b3 = parseInt(seq.slice(i + 7, i + 9), 16); + const b4 = parseInt(seq.slice(i + 10, i + 12), 16); + if ((b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80 && (b4 & 0xC0) === 0x80) { + let chr = b1 << 18 & 0x1C0000 | b2 << 12 & 0x3F000 | b3 << 6 & 0xFC0 | b4 & 0x3F; + if (chr < 0x10000 || chr > 0x10FFFF) { + result += '\ufffd\ufffd\ufffd\ufffd'; + } else { + chr -= 0x10000; + result += String.fromCharCode(0xD800 + (chr >> 10), 0xDC00 + (chr & 0x3FF)); + } + i += 9; + continue; + } + } + result += '\ufffd'; + } + return result; + }); +} +decode.defaultChars = ';/?:@&=+$,#'; +decode.componentChars = ''; +const encodeCache = {}; + +// Create a lookup array where anything but characters in `chars` string +// and alphanumeric chars is percent-encoded. +// +function getEncodeCache(exclude) { + let cache = encodeCache[exclude]; + if (cache) { + return cache; + } + cache = encodeCache[exclude] = []; + for (let i = 0; i < 128; i++) { + const ch = String.fromCharCode(i); + if (/^[0-9a-z]$/i.test(ch)) { + // always allow unencoded alphanumeric characters + cache.push(ch); + } else { + cache.push('%' + ('0' + i.toString(16).toUpperCase()).slice(-2)); + } + } + for (let i = 0; i < exclude.length; i++) { + cache[exclude.charCodeAt(i)] = exclude[i]; + } + return cache; +} + +// Encode unsafe characters with percent-encoding, skipping already +// encoded sequences. +// +// - string - string to encode +// - exclude - list of characters to ignore (in addition to a-zA-Z0-9) +// - keepEscaped - don't encode '%' in a correct escape sequence (default: true) +// +function encode(string, exclude, keepEscaped) { + if (typeof exclude !== 'string') { + // encode(string, keepEscaped) + keepEscaped = exclude; + exclude = encode.defaultChars; + } + if (typeof keepEscaped === 'undefined') { + keepEscaped = true; + } + const cache = getEncodeCache(exclude); + let result = ''; + for (let i = 0, l = string.length; i < l; i++) { + const code = string.charCodeAt(i); + if (keepEscaped && code === 0x25 /* % */ && i + 2 < l) { + if (/^[0-9a-f]{2}$/i.test(string.slice(i + 1, i + 3))) { + result += string.slice(i, i + 3); + i += 2; + continue; + } + } + if (code < 128) { + result += cache[code]; + continue; + } + if (code >= 0xD800 && code <= 0xDFFF) { + if (code >= 0xD800 && code <= 0xDBFF && i + 1 < l) { + const nextCode = string.charCodeAt(i + 1); + if (nextCode >= 0xDC00 && nextCode <= 0xDFFF) { + result += encodeURIComponent(string[i] + string[i + 1]); + i++; + continue; + } + } + result += '%EF%BF%BD'; + continue; + } + result += encodeURIComponent(string[i]); + } + return result; +} +encode.defaultChars = ";/?:@&=+$,-_.!~*'()#"; +encode.componentChars = "-_.!~*'()"; +function format(url) { + let result = ''; + result += url.protocol || ''; + result += url.slashes ? '//' : ''; + result += url.auth ? url.auth + '@' : ''; + if (url.hostname && url.hostname.indexOf(':') !== -1) { + // ipv6 address + result += '[' + url.hostname + ']'; + } else { + result += url.hostname || ''; + } + result += url.port ? ':' + url.port : ''; + result += url.pathname || ''; + result += url.search || ''; + result += url.hash || ''; + return result; +} + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// +// Changes from joyent/node: +// +// 1. No leading slash in paths, +// e.g. in `url.parse('http://foo?bar')` pathname is ``, not `/` +// +// 2. Backslashes are not replaced with slashes, +// so `http:\\example.org\` is treated like a relative path +// +// 3. Trailing colon is treated like a part of the path, +// i.e. in `http://example.org:foo` pathname is `:foo` +// +// 4. Nothing is URL-encoded in the resulting object, +// (in joyent/node some chars in auth and paths are encoded) +// +// 5. `url.parse()` does not have `parseQueryString` argument +// +// 6. Removed extraneous result properties: `host`, `path`, `query`, etc., +// which can be constructed using other parts of the url. +// + +function Url() { + this.protocol = null; + this.slashes = null; + this.auth = null; + this.port = null; + this.hostname = null; + this.hash = null; + this.search = null; + this.pathname = null; +} + +// Reference: RFC 3986, RFC 1808, RFC 2396 + +// define these here so at least they only have to be +// compiled once on the first module load. +const protocolPattern = /^([a-z0-9.+-]+:)/i; +const portPattern = /:[0-9]*$/; + +// Special case for a simple path URL +/* eslint-disable-next-line no-useless-escape */ +const simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/; + +// RFC 2396: characters reserved for delimiting URLs. +// We actually just auto-escape these. +const delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t']; + +// RFC 2396: characters not allowed for various reasons. +const unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims); + +// Allowed by RFCs, but cause of XSS attacks. Always escape these. +const autoEscape = ['\''].concat(unwise); +// Characters that are never ever allowed in a hostname. +// Note that any invalid chars are also handled, but these +// are the ones that are *expected* to be seen, so we fast-path +// them. +const nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape); +const hostEndingChars = ['/', '?', '#']; +const hostnameMaxLen = 255; +const hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/; +const hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/; +// protocols that can allow "unsafe" and "unwise" chars. +// protocols that never have a hostname. +const hostlessProtocol = { + javascript: true, + 'javascript:': true +}; +// protocols that always contain a // bit. +const slashedProtocol = { + http: true, + https: true, + ftp: true, + gopher: true, + file: true, + 'http:': true, + 'https:': true, + 'ftp:': true, + 'gopher:': true, + 'file:': true +}; +function urlParse(url, slashesDenoteHost) { + if (url && url instanceof Url) return url; + const u = new Url(); + u.parse(url, slashesDenoteHost); + return u; +} +Url.prototype.parse = function (url, slashesDenoteHost) { + let lowerProto, hec, slashes; + let rest = url; + + // trim before proceeding. + // This is to support parse stuff like " http://foo.com \n" + rest = rest.trim(); + if (!slashesDenoteHost && url.split('#').length === 1) { + // Try fast path regexp + const simplePath = simplePathPattern.exec(rest); + if (simplePath) { + this.pathname = simplePath[1]; + if (simplePath[2]) { + this.search = simplePath[2]; + } + return this; + } + } + let proto = protocolPattern.exec(rest); + if (proto) { + proto = proto[0]; + lowerProto = proto.toLowerCase(); + this.protocol = proto; + rest = rest.substr(proto.length); + } + + // figure out if it's got a host + // user@server is *always* interpreted as a hostname, and url + // resolution will treat //foo/bar as host=foo,path=bar because that's + // how the browser resolves relative URLs. + /* eslint-disable-next-line no-useless-escape */ + if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { + slashes = rest.substr(0, 2) === '//'; + if (slashes && !(proto && hostlessProtocol[proto])) { + rest = rest.substr(2); + this.slashes = true; + } + } + if (!hostlessProtocol[proto] && (slashes || proto && !slashedProtocol[proto])) { + // there's a hostname. + // the first instance of /, ?, ;, or # ends the host. + // + // If there is an @ in the hostname, then non-host chars *are* allowed + // to the left of the last @ sign, unless some host-ending character + // comes *before* the @-sign. + // URLs are obnoxious. + // + // ex: + // http://a@b@c/ => user:a@b host:c + // http://a@b?@c => user:a host:c path:/?@c + + // v0.12 TODO(isaacs): This is not quite how Chrome does things. + // Review our test case against browsers more comprehensively. + + // find the first instance of any hostEndingChars + let hostEnd = -1; + for (let i = 0; i < hostEndingChars.length; i++) { + hec = rest.indexOf(hostEndingChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) { + hostEnd = hec; + } + } + + // at this point, either we have an explicit point where the + // auth portion cannot go past, or the last @ char is the decider. + let auth, atSign; + if (hostEnd === -1) { + // atSign can be anywhere. + atSign = rest.lastIndexOf('@'); + } else { + // atSign must be in auth portion. + // http://a@b/c@d => host:b auth:a path:/c@d + atSign = rest.lastIndexOf('@', hostEnd); + } + + // Now we have a portion which is definitely the auth. + // Pull that off. + if (atSign !== -1) { + auth = rest.slice(0, atSign); + rest = rest.slice(atSign + 1); + this.auth = auth; + } + + // the host is the remaining to the left of the first non-host char + hostEnd = -1; + for (let i = 0; i < nonHostChars.length; i++) { + hec = rest.indexOf(nonHostChars[i]); + if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) { + hostEnd = hec; + } + } + // if we still have not hit it, then the entire thing is a host. + if (hostEnd === -1) { + hostEnd = rest.length; + } + if (rest[hostEnd - 1] === ':') { + hostEnd--; + } + const host = rest.slice(0, hostEnd); + rest = rest.slice(hostEnd); + + // pull out port. + this.parseHost(host); + + // we've indicated that there is a hostname, + // so even if it's empty, it has to be present. + this.hostname = this.hostname || ''; + + // if hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + const ipv6Hostname = this.hostname[0] === '[' && this.hostname[this.hostname.length - 1] === ']'; + + // validate a little. + if (!ipv6Hostname) { + const hostparts = this.hostname.split(/\./); + for (let i = 0, l = hostparts.length; i < l; i++) { + const part = hostparts[i]; + if (!part) { + continue; + } + if (!part.match(hostnamePartPattern)) { + let newpart = ''; + for (let j = 0, k = part.length; j < k; j++) { + if (part.charCodeAt(j) > 127) { + // we replace non-ASCII char with a temporary placeholder + // we need this to make sure size of hostname is not + // broken by replacing non-ASCII by nothing + newpart += 'x'; + } else { + newpart += part[j]; + } + } + // we test again with ASCII char only + if (!newpart.match(hostnamePartPattern)) { + const validParts = hostparts.slice(0, i); + const notHost = hostparts.slice(i + 1); + const bit = part.match(hostnamePartStart); + if (bit) { + validParts.push(bit[1]); + notHost.unshift(bit[2]); + } + if (notHost.length) { + rest = notHost.join('.') + rest; + } + this.hostname = validParts.join('.'); + break; + } + } + } + } + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ''; + } + + // strip [ and ] from the hostname + // the host field still retains them, though + if (ipv6Hostname) { + this.hostname = this.hostname.substr(1, this.hostname.length - 2); + } + } + + // chop off from the tail first. + const hash = rest.indexOf('#'); + if (hash !== -1) { + // got a fragment string. + this.hash = rest.substr(hash); + rest = rest.slice(0, hash); + } + const qm = rest.indexOf('?'); + if (qm !== -1) { + this.search = rest.substr(qm); + rest = rest.slice(0, qm); + } + if (rest) { + this.pathname = rest; + } + if (slashedProtocol[lowerProto] && this.hostname && !this.pathname) { + this.pathname = ''; + } + return this; +}; +Url.prototype.parseHost = function (host) { + let port = portPattern.exec(host); + if (port) { + port = port[0]; + if (port !== ':') { + this.port = port.substr(1); + } + host = host.substr(0, host.length - port.length); + } + if (host) { + this.hostname = host; + } +}; +exports.decode = decode; +exports.encode = encode; +exports.format = format; +exports.parse = urlParse; + +/***/ }), + +/***/ "../node_modules/uc.micro/build/index.cjs.js": +/*!***************************************************!*\ + !*** ../node_modules/uc.micro/build/index.cjs.js ***! + \***************************************************/ +/***/ (function(__unused_webpack_module, exports) { + + + +var regex$5 = /[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; +var regex$4 = /[\0-\x1F\x7F-\x9F]/; +var regex$3 = /[\xAD\u0600-\u0605\u061C\u06DD\u070F\u0890\u0891\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804[\uDCBD\uDCCD]|\uD80D[\uDC30-\uDC3F]|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/; +var regex$2 = /[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061D-\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C77\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1B7D\u1B7E\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4F\u2E52-\u2E5D\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDEAD\uDF55-\uDF59\uDF86-\uDF89]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5A\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDEB9\uDF3C-\uDF3E]|\uD806[\uDC3B\uDD44-\uDD46\uDDE2\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2\uDF00-\uDF09]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8\uDF43-\uDF4F\uDFFF]|\uD809[\uDC70-\uDC74]|\uD80B[\uDFF1\uDFF2]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDE97-\uDE9A\uDFE2]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/; +var regex$1 = /[\$\+<->\^`\|~\xA2-\xA6\xA8\xA9\xAC\xAE-\xB1\xB4\xB8\xD7\xF7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0384\u0385\u03F6\u0482\u058D-\u058F\u0606-\u0608\u060B\u060E\u060F\u06DE\u06E9\u06FD\u06FE\u07F6\u07FE\u07FF\u0888\u09F2\u09F3\u09FA\u09FB\u0AF1\u0B70\u0BF3-\u0BFA\u0C7F\u0D4F\u0D79\u0E3F\u0F01-\u0F03\u0F13\u0F15-\u0F17\u0F1A-\u0F1F\u0F34\u0F36\u0F38\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE\u0FCF\u0FD5-\u0FD8\u109E\u109F\u1390-\u1399\u166D\u17DB\u1940\u19DE-\u19FF\u1B61-\u1B6A\u1B74-\u1B7C\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2044\u2052\u207A-\u207C\u208A-\u208C\u20A0-\u20C0\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F\u218A\u218B\u2190-\u2307\u230C-\u2328\u232B-\u2426\u2440-\u244A\u249C-\u24E9\u2500-\u2767\u2794-\u27C4\u27C7-\u27E5\u27F0-\u2982\u2999-\u29D7\u29DC-\u29FB\u29FE-\u2B73\u2B76-\u2B95\u2B97-\u2BFF\u2CE5-\u2CEA\u2E50\u2E51\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFF\u3004\u3012\u3013\u3020\u3036\u3037\u303E\u303F\u309B\u309C\u3190\u3191\u3196-\u319F\u31C0-\u31E3\u31EF\u3200-\u321E\u322A-\u3247\u3250\u3260-\u327F\u328A-\u32B0\u32C0-\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA700-\uA716\uA720\uA721\uA789\uA78A\uA828-\uA82B\uA836-\uA839\uAA77-\uAA79\uAB5B\uAB6A\uAB6B\uFB29\uFBB2-\uFBC2\uFD40-\uFD4F\uFDCF\uFDFC-\uFDFF\uFE62\uFE64-\uFE66\uFE69\uFF04\uFF0B\uFF1C-\uFF1E\uFF3E\uFF40\uFF5C\uFF5E\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFFC\uFFFD]|\uD800[\uDD37-\uDD3F\uDD79-\uDD89\uDD8C-\uDD8E\uDD90-\uDD9C\uDDA0\uDDD0-\uDDFC]|\uD802[\uDC77\uDC78\uDEC8]|\uD805\uDF3F|\uD807[\uDFD5-\uDFF1]|\uD81A[\uDF3C-\uDF3F\uDF45]|\uD82F\uDC9C|\uD833[\uDF50-\uDFC3]|\uD834[\uDC00-\uDCF5\uDD00-\uDD26\uDD29-\uDD64\uDD6A-\uDD6C\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDDEA\uDE00-\uDE41\uDE45\uDF00-\uDF56]|\uD835[\uDEC1\uDEDB\uDEFB\uDF15\uDF35\uDF4F\uDF6F\uDF89\uDFA9\uDFC3]|\uD836[\uDC00-\uDDFF\uDE37-\uDE3A\uDE6D-\uDE74\uDE76-\uDE83\uDE85\uDE86]|\uD838[\uDD4F\uDEFF]|\uD83B[\uDCAC\uDCB0\uDD2E\uDEF0\uDEF1]|\uD83C[\uDC00-\uDC2B\uDC30-\uDC93\uDCA0-\uDCAE\uDCB1-\uDCBF\uDCC1-\uDCCF\uDCD1-\uDCF5\uDD0D-\uDDAD\uDDE6-\uDE02\uDE10-\uDE3B\uDE40-\uDE48\uDE50\uDE51\uDE60-\uDE65\uDF00-\uDFFF]|\uD83D[\uDC00-\uDED7\uDEDC-\uDEEC\uDEF0-\uDEFC\uDF00-\uDF76\uDF7B-\uDFD9\uDFE0-\uDFEB\uDFF0]|\uD83E[\uDC00-\uDC0B\uDC10-\uDC47\uDC50-\uDC59\uDC60-\uDC87\uDC90-\uDCAD\uDCB0\uDCB1\uDD00-\uDE53\uDE60-\uDE6D\uDE70-\uDE7C\uDE80-\uDE88\uDE90-\uDEBD\uDEBF-\uDEC5\uDECE-\uDEDB\uDEE0-\uDEE8\uDEF0-\uDEF8\uDF00-\uDF92\uDF94-\uDFCA]/; +var regex = /[ \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/; +exports.Any = regex$5; +exports.Cc = regex$4; +exports.Cf = regex$3; +exports.P = regex$2; +exports.S = regex$1; +exports.Z = regex; + +/***/ }), + /***/ "./components/GraphiQL.tsx": /*!*********************************!*\ !*** ./components/GraphiQL.tsx ***! @@ -72235,11 +72914,23 @@ var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); var _react2 = __webpack_require__(/*! @graphiql/react */ "../../graphiql-react/dist/index.js"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } -function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } +function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /** + * Copyright (c) 2020 GraphQL Contributors. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ const majorVersion = parseInt(_react.default.version.slice(0, 2), 10); if (majorVersion < 16) { throw new Error(['GraphiQL 0.18.0 and after is not compatible with React 15 or below.', 'If you are using a CDN source (jsdelivr, unpkg, etc), follow this example:', 'https://github.com/graphql/graphiql/blob/master/examples/graphiql-cdn/index.html#L49'].join('\n')); } + +/** + * API docs for this live here: + * + * https://graphiql-test.netlify.app/typedoc/modules/graphiql.html#graphiqlprops + */ + /** * The top-level React component for GraphiQL, intended to encompass the entire * browser viewport. @@ -72248,6 +72939,7 @@ if (majorVersion < 16) { */ function GraphiQL(_ref) { + var _props$disableTabs; let { dangerouslyAssumeSchemaIsValid, defaultQuery, @@ -72309,7 +73001,8 @@ function GraphiQL(_ref) { validationRules: validationRules, variables: variables }, /*#__PURE__*/_react.default.createElement(GraphiQLInterface, _extends({ - showPersistHeadersSettings: shouldPersistHeaders !== false + showPersistHeadersSettings: shouldPersistHeaders !== false, + disableTabs: (_props$disableTabs = props.disableTabs) !== null && _props$disableTabs !== void 0 ? _props$disableTabs : false }, props))); } @@ -72543,7 +73236,7 @@ function GraphiQLInterface(props) { className: "graphiql-sessions" }, /*#__PURE__*/_react.default.createElement("div", { className: "graphiql-session-header" - }, /*#__PURE__*/_react.default.createElement(_react2.Tabs, { + }, props.disableTabs ? null : /*#__PURE__*/_react.default.createElement(_react2.Tabs, { values: editorContext.tabs, onReorder: handleReorder, "aria-label": "Select active operation" @@ -83529,16 +84222,6 @@ function _extends() { } module.exports = _extends, module.exports.__esModule = true, module.exports["default"] = module.exports; -/***/ }), - -/***/ "../../../node_modules/entities/lib/maps/entities.json": -/*!*************************************************************!*\ - !*** ../../../node_modules/entities/lib/maps/entities.json ***! - \*************************************************************/ -/***/ (function(module) { - -module.exports = JSON.parse('{"Aacute":"Á","aacute":"á","Abreve":"Ă","abreve":"ă","ac":"∾","acd":"∿","acE":"∾̳","Acirc":"Â","acirc":"â","acute":"´","Acy":"А","acy":"а","AElig":"Æ","aelig":"æ","af":"⁡","Afr":"𝔄","afr":"𝔞","Agrave":"À","agrave":"à","alefsym":"ℵ","aleph":"ℵ","Alpha":"Α","alpha":"α","Amacr":"Ā","amacr":"ā","amalg":"⨿","amp":"&","AMP":"&","andand":"⩕","And":"⩓","and":"∧","andd":"⩜","andslope":"⩘","andv":"⩚","ang":"∠","ange":"⦤","angle":"∠","angmsdaa":"⦨","angmsdab":"⦩","angmsdac":"⦪","angmsdad":"⦫","angmsdae":"⦬","angmsdaf":"⦭","angmsdag":"⦮","angmsdah":"⦯","angmsd":"∡","angrt":"∟","angrtvb":"⊾","angrtvbd":"⦝","angsph":"∢","angst":"Å","angzarr":"⍼","Aogon":"Ą","aogon":"ą","Aopf":"𝔸","aopf":"𝕒","apacir":"⩯","ap":"≈","apE":"⩰","ape":"≊","apid":"≋","apos":"\'","ApplyFunction":"⁡","approx":"≈","approxeq":"≊","Aring":"Å","aring":"å","Ascr":"𝒜","ascr":"𝒶","Assign":"≔","ast":"*","asymp":"≈","asympeq":"≍","Atilde":"Ã","atilde":"ã","Auml":"Ä","auml":"ä","awconint":"∳","awint":"⨑","backcong":"≌","backepsilon":"϶","backprime":"‵","backsim":"∽","backsimeq":"⋍","Backslash":"∖","Barv":"⫧","barvee":"⊽","barwed":"⌅","Barwed":"⌆","barwedge":"⌅","bbrk":"⎵","bbrktbrk":"⎶","bcong":"≌","Bcy":"Б","bcy":"б","bdquo":"„","becaus":"∵","because":"∵","Because":"∵","bemptyv":"⦰","bepsi":"϶","bernou":"ℬ","Bernoullis":"ℬ","Beta":"Β","beta":"β","beth":"ℶ","between":"≬","Bfr":"𝔅","bfr":"𝔟","bigcap":"⋂","bigcirc":"◯","bigcup":"⋃","bigodot":"⨀","bigoplus":"⨁","bigotimes":"⨂","bigsqcup":"⨆","bigstar":"★","bigtriangledown":"▽","bigtriangleup":"△","biguplus":"⨄","bigvee":"⋁","bigwedge":"⋀","bkarow":"⤍","blacklozenge":"⧫","blacksquare":"▪","blacktriangle":"▴","blacktriangledown":"▾","blacktriangleleft":"◂","blacktriangleright":"▸","blank":"␣","blk12":"▒","blk14":"░","blk34":"▓","block":"█","bne":"=⃥","bnequiv":"≡⃥","bNot":"⫭","bnot":"⌐","Bopf":"𝔹","bopf":"𝕓","bot":"⊥","bottom":"⊥","bowtie":"⋈","boxbox":"⧉","boxdl":"┐","boxdL":"╕","boxDl":"╖","boxDL":"╗","boxdr":"┌","boxdR":"╒","boxDr":"╓","boxDR":"╔","boxh":"─","boxH":"═","boxhd":"┬","boxHd":"╤","boxhD":"╥","boxHD":"╦","boxhu":"┴","boxHu":"╧","boxhU":"╨","boxHU":"╩","boxminus":"⊟","boxplus":"⊞","boxtimes":"⊠","boxul":"┘","boxuL":"╛","boxUl":"╜","boxUL":"╝","boxur":"└","boxuR":"╘","boxUr":"╙","boxUR":"╚","boxv":"│","boxV":"║","boxvh":"┼","boxvH":"╪","boxVh":"╫","boxVH":"╬","boxvl":"┤","boxvL":"╡","boxVl":"╢","boxVL":"╣","boxvr":"├","boxvR":"╞","boxVr":"╟","boxVR":"╠","bprime":"‵","breve":"˘","Breve":"˘","brvbar":"¦","bscr":"𝒷","Bscr":"ℬ","bsemi":"⁏","bsim":"∽","bsime":"⋍","bsolb":"⧅","bsol":"\\\\","bsolhsub":"⟈","bull":"•","bullet":"•","bump":"≎","bumpE":"⪮","bumpe":"≏","Bumpeq":"≎","bumpeq":"≏","Cacute":"Ć","cacute":"ć","capand":"⩄","capbrcup":"⩉","capcap":"⩋","cap":"∩","Cap":"⋒","capcup":"⩇","capdot":"⩀","CapitalDifferentialD":"ⅅ","caps":"∩︀","caret":"⁁","caron":"ˇ","Cayleys":"ℭ","ccaps":"⩍","Ccaron":"Č","ccaron":"č","Ccedil":"Ç","ccedil":"ç","Ccirc":"Ĉ","ccirc":"ĉ","Cconint":"∰","ccups":"⩌","ccupssm":"⩐","Cdot":"Ċ","cdot":"ċ","cedil":"¸","Cedilla":"¸","cemptyv":"⦲","cent":"¢","centerdot":"·","CenterDot":"·","cfr":"𝔠","Cfr":"ℭ","CHcy":"Ч","chcy":"ч","check":"✓","checkmark":"✓","Chi":"Χ","chi":"χ","circ":"ˆ","circeq":"≗","circlearrowleft":"↺","circlearrowright":"↻","circledast":"⊛","circledcirc":"⊚","circleddash":"⊝","CircleDot":"⊙","circledR":"®","circledS":"Ⓢ","CircleMinus":"⊖","CirclePlus":"⊕","CircleTimes":"⊗","cir":"○","cirE":"⧃","cire":"≗","cirfnint":"⨐","cirmid":"⫯","cirscir":"⧂","ClockwiseContourIntegral":"∲","CloseCurlyDoubleQuote":"”","CloseCurlyQuote":"’","clubs":"♣","clubsuit":"♣","colon":":","Colon":"∷","Colone":"⩴","colone":"≔","coloneq":"≔","comma":",","commat":"@","comp":"∁","compfn":"∘","complement":"∁","complexes":"ℂ","cong":"≅","congdot":"⩭","Congruent":"≡","conint":"∮","Conint":"∯","ContourIntegral":"∮","copf":"𝕔","Copf":"ℂ","coprod":"∐","Coproduct":"∐","copy":"©","COPY":"©","copysr":"℗","CounterClockwiseContourIntegral":"∳","crarr":"↵","cross":"✗","Cross":"⨯","Cscr":"𝒞","cscr":"𝒸","csub":"⫏","csube":"⫑","csup":"⫐","csupe":"⫒","ctdot":"⋯","cudarrl":"⤸","cudarrr":"⤵","cuepr":"⋞","cuesc":"⋟","cularr":"↶","cularrp":"⤽","cupbrcap":"⩈","cupcap":"⩆","CupCap":"≍","cup":"∪","Cup":"⋓","cupcup":"⩊","cupdot":"⊍","cupor":"⩅","cups":"∪︀","curarr":"↷","curarrm":"⤼","curlyeqprec":"⋞","curlyeqsucc":"⋟","curlyvee":"⋎","curlywedge":"⋏","curren":"¤","curvearrowleft":"↶","curvearrowright":"↷","cuvee":"⋎","cuwed":"⋏","cwconint":"∲","cwint":"∱","cylcty":"⌭","dagger":"†","Dagger":"‡","daleth":"ℸ","darr":"↓","Darr":"↡","dArr":"⇓","dash":"‐","Dashv":"⫤","dashv":"⊣","dbkarow":"⤏","dblac":"˝","Dcaron":"Ď","dcaron":"ď","Dcy":"Д","dcy":"д","ddagger":"‡","ddarr":"⇊","DD":"ⅅ","dd":"ⅆ","DDotrahd":"⤑","ddotseq":"⩷","deg":"°","Del":"∇","Delta":"Δ","delta":"δ","demptyv":"⦱","dfisht":"⥿","Dfr":"𝔇","dfr":"𝔡","dHar":"⥥","dharl":"⇃","dharr":"⇂","DiacriticalAcute":"´","DiacriticalDot":"˙","DiacriticalDoubleAcute":"˝","DiacriticalGrave":"`","DiacriticalTilde":"˜","diam":"⋄","diamond":"⋄","Diamond":"⋄","diamondsuit":"♦","diams":"♦","die":"¨","DifferentialD":"ⅆ","digamma":"ϝ","disin":"⋲","div":"÷","divide":"÷","divideontimes":"⋇","divonx":"⋇","DJcy":"Ђ","djcy":"ђ","dlcorn":"⌞","dlcrop":"⌍","dollar":"$","Dopf":"𝔻","dopf":"𝕕","Dot":"¨","dot":"˙","DotDot":"⃜","doteq":"≐","doteqdot":"≑","DotEqual":"≐","dotminus":"∸","dotplus":"∔","dotsquare":"⊡","doublebarwedge":"⌆","DoubleContourIntegral":"∯","DoubleDot":"¨","DoubleDownArrow":"⇓","DoubleLeftArrow":"⇐","DoubleLeftRightArrow":"⇔","DoubleLeftTee":"⫤","DoubleLongLeftArrow":"⟸","DoubleLongLeftRightArrow":"⟺","DoubleLongRightArrow":"⟹","DoubleRightArrow":"⇒","DoubleRightTee":"⊨","DoubleUpArrow":"⇑","DoubleUpDownArrow":"⇕","DoubleVerticalBar":"∥","DownArrowBar":"⤓","downarrow":"↓","DownArrow":"↓","Downarrow":"⇓","DownArrowUpArrow":"⇵","DownBreve":"̑","downdownarrows":"⇊","downharpoonleft":"⇃","downharpoonright":"⇂","DownLeftRightVector":"⥐","DownLeftTeeVector":"⥞","DownLeftVectorBar":"⥖","DownLeftVector":"↽","DownRightTeeVector":"⥟","DownRightVectorBar":"⥗","DownRightVector":"⇁","DownTeeArrow":"↧","DownTee":"⊤","drbkarow":"⤐","drcorn":"⌟","drcrop":"⌌","Dscr":"𝒟","dscr":"𝒹","DScy":"Ѕ","dscy":"ѕ","dsol":"⧶","Dstrok":"Đ","dstrok":"đ","dtdot":"⋱","dtri":"▿","dtrif":"▾","duarr":"⇵","duhar":"⥯","dwangle":"⦦","DZcy":"Џ","dzcy":"џ","dzigrarr":"⟿","Eacute":"É","eacute":"é","easter":"⩮","Ecaron":"Ě","ecaron":"ě","Ecirc":"Ê","ecirc":"ê","ecir":"≖","ecolon":"≕","Ecy":"Э","ecy":"э","eDDot":"⩷","Edot":"Ė","edot":"ė","eDot":"≑","ee":"ⅇ","efDot":"≒","Efr":"𝔈","efr":"𝔢","eg":"⪚","Egrave":"È","egrave":"è","egs":"⪖","egsdot":"⪘","el":"⪙","Element":"∈","elinters":"⏧","ell":"ℓ","els":"⪕","elsdot":"⪗","Emacr":"Ē","emacr":"ē","empty":"∅","emptyset":"∅","EmptySmallSquare":"◻","emptyv":"∅","EmptyVerySmallSquare":"▫","emsp13":" ","emsp14":" ","emsp":" ","ENG":"Ŋ","eng":"ŋ","ensp":" ","Eogon":"Ę","eogon":"ę","Eopf":"𝔼","eopf":"𝕖","epar":"⋕","eparsl":"⧣","eplus":"⩱","epsi":"ε","Epsilon":"Ε","epsilon":"ε","epsiv":"ϵ","eqcirc":"≖","eqcolon":"≕","eqsim":"≂","eqslantgtr":"⪖","eqslantless":"⪕","Equal":"⩵","equals":"=","EqualTilde":"≂","equest":"≟","Equilibrium":"⇌","equiv":"≡","equivDD":"⩸","eqvparsl":"⧥","erarr":"⥱","erDot":"≓","escr":"ℯ","Escr":"ℰ","esdot":"≐","Esim":"⩳","esim":"≂","Eta":"Η","eta":"η","ETH":"Ð","eth":"ð","Euml":"Ë","euml":"ë","euro":"€","excl":"!","exist":"∃","Exists":"∃","expectation":"ℰ","exponentiale":"ⅇ","ExponentialE":"ⅇ","fallingdotseq":"≒","Fcy":"Ф","fcy":"ф","female":"♀","ffilig":"ffi","fflig":"ff","ffllig":"ffl","Ffr":"𝔉","ffr":"𝔣","filig":"fi","FilledSmallSquare":"◼","FilledVerySmallSquare":"▪","fjlig":"fj","flat":"♭","fllig":"fl","fltns":"▱","fnof":"ƒ","Fopf":"𝔽","fopf":"𝕗","forall":"∀","ForAll":"∀","fork":"⋔","forkv":"⫙","Fouriertrf":"ℱ","fpartint":"⨍","frac12":"½","frac13":"⅓","frac14":"¼","frac15":"⅕","frac16":"⅙","frac18":"⅛","frac23":"⅔","frac25":"⅖","frac34":"¾","frac35":"⅗","frac38":"⅜","frac45":"⅘","frac56":"⅚","frac58":"⅝","frac78":"⅞","frasl":"⁄","frown":"⌢","fscr":"𝒻","Fscr":"ℱ","gacute":"ǵ","Gamma":"Γ","gamma":"γ","Gammad":"Ϝ","gammad":"ϝ","gap":"⪆","Gbreve":"Ğ","gbreve":"ğ","Gcedil":"Ģ","Gcirc":"Ĝ","gcirc":"ĝ","Gcy":"Г","gcy":"г","Gdot":"Ġ","gdot":"ġ","ge":"≥","gE":"≧","gEl":"⪌","gel":"⋛","geq":"≥","geqq":"≧","geqslant":"⩾","gescc":"⪩","ges":"⩾","gesdot":"⪀","gesdoto":"⪂","gesdotol":"⪄","gesl":"⋛︀","gesles":"⪔","Gfr":"𝔊","gfr":"𝔤","gg":"≫","Gg":"⋙","ggg":"⋙","gimel":"ℷ","GJcy":"Ѓ","gjcy":"ѓ","gla":"⪥","gl":"≷","glE":"⪒","glj":"⪤","gnap":"⪊","gnapprox":"⪊","gne":"⪈","gnE":"≩","gneq":"⪈","gneqq":"≩","gnsim":"⋧","Gopf":"𝔾","gopf":"𝕘","grave":"`","GreaterEqual":"≥","GreaterEqualLess":"⋛","GreaterFullEqual":"≧","GreaterGreater":"⪢","GreaterLess":"≷","GreaterSlantEqual":"⩾","GreaterTilde":"≳","Gscr":"𝒢","gscr":"ℊ","gsim":"≳","gsime":"⪎","gsiml":"⪐","gtcc":"⪧","gtcir":"⩺","gt":">","GT":">","Gt":"≫","gtdot":"⋗","gtlPar":"⦕","gtquest":"⩼","gtrapprox":"⪆","gtrarr":"⥸","gtrdot":"⋗","gtreqless":"⋛","gtreqqless":"⪌","gtrless":"≷","gtrsim":"≳","gvertneqq":"≩︀","gvnE":"≩︀","Hacek":"ˇ","hairsp":" ","half":"½","hamilt":"ℋ","HARDcy":"Ъ","hardcy":"ъ","harrcir":"⥈","harr":"↔","hArr":"⇔","harrw":"↭","Hat":"^","hbar":"ℏ","Hcirc":"Ĥ","hcirc":"ĥ","hearts":"♥","heartsuit":"♥","hellip":"…","hercon":"⊹","hfr":"𝔥","Hfr":"ℌ","HilbertSpace":"ℋ","hksearow":"⤥","hkswarow":"⤦","hoarr":"⇿","homtht":"∻","hookleftarrow":"↩","hookrightarrow":"↪","hopf":"𝕙","Hopf":"ℍ","horbar":"―","HorizontalLine":"─","hscr":"𝒽","Hscr":"ℋ","hslash":"ℏ","Hstrok":"Ħ","hstrok":"ħ","HumpDownHump":"≎","HumpEqual":"≏","hybull":"⁃","hyphen":"‐","Iacute":"Í","iacute":"í","ic":"⁣","Icirc":"Î","icirc":"î","Icy":"И","icy":"и","Idot":"İ","IEcy":"Е","iecy":"е","iexcl":"¡","iff":"⇔","ifr":"𝔦","Ifr":"ℑ","Igrave":"Ì","igrave":"ì","ii":"ⅈ","iiiint":"⨌","iiint":"∭","iinfin":"⧜","iiota":"℩","IJlig":"IJ","ijlig":"ij","Imacr":"Ī","imacr":"ī","image":"ℑ","ImaginaryI":"ⅈ","imagline":"ℐ","imagpart":"ℑ","imath":"ı","Im":"ℑ","imof":"⊷","imped":"Ƶ","Implies":"⇒","incare":"℅","in":"∈","infin":"∞","infintie":"⧝","inodot":"ı","intcal":"⊺","int":"∫","Int":"∬","integers":"ℤ","Integral":"∫","intercal":"⊺","Intersection":"⋂","intlarhk":"⨗","intprod":"⨼","InvisibleComma":"⁣","InvisibleTimes":"⁢","IOcy":"Ё","iocy":"ё","Iogon":"Į","iogon":"į","Iopf":"𝕀","iopf":"𝕚","Iota":"Ι","iota":"ι","iprod":"⨼","iquest":"¿","iscr":"𝒾","Iscr":"ℐ","isin":"∈","isindot":"⋵","isinE":"⋹","isins":"⋴","isinsv":"⋳","isinv":"∈","it":"⁢","Itilde":"Ĩ","itilde":"ĩ","Iukcy":"І","iukcy":"і","Iuml":"Ï","iuml":"ï","Jcirc":"Ĵ","jcirc":"ĵ","Jcy":"Й","jcy":"й","Jfr":"𝔍","jfr":"𝔧","jmath":"ȷ","Jopf":"𝕁","jopf":"𝕛","Jscr":"𝒥","jscr":"𝒿","Jsercy":"Ј","jsercy":"ј","Jukcy":"Є","jukcy":"є","Kappa":"Κ","kappa":"κ","kappav":"ϰ","Kcedil":"Ķ","kcedil":"ķ","Kcy":"К","kcy":"к","Kfr":"𝔎","kfr":"𝔨","kgreen":"ĸ","KHcy":"Х","khcy":"х","KJcy":"Ќ","kjcy":"ќ","Kopf":"𝕂","kopf":"𝕜","Kscr":"𝒦","kscr":"𝓀","lAarr":"⇚","Lacute":"Ĺ","lacute":"ĺ","laemptyv":"⦴","lagran":"ℒ","Lambda":"Λ","lambda":"λ","lang":"⟨","Lang":"⟪","langd":"⦑","langle":"⟨","lap":"⪅","Laplacetrf":"ℒ","laquo":"«","larrb":"⇤","larrbfs":"⤟","larr":"←","Larr":"↞","lArr":"⇐","larrfs":"⤝","larrhk":"↩","larrlp":"↫","larrpl":"⤹","larrsim":"⥳","larrtl":"↢","latail":"⤙","lAtail":"⤛","lat":"⪫","late":"⪭","lates":"⪭︀","lbarr":"⤌","lBarr":"⤎","lbbrk":"❲","lbrace":"{","lbrack":"[","lbrke":"⦋","lbrksld":"⦏","lbrkslu":"⦍","Lcaron":"Ľ","lcaron":"ľ","Lcedil":"Ļ","lcedil":"ļ","lceil":"⌈","lcub":"{","Lcy":"Л","lcy":"л","ldca":"⤶","ldquo":"“","ldquor":"„","ldrdhar":"⥧","ldrushar":"⥋","ldsh":"↲","le":"≤","lE":"≦","LeftAngleBracket":"⟨","LeftArrowBar":"⇤","leftarrow":"←","LeftArrow":"←","Leftarrow":"⇐","LeftArrowRightArrow":"⇆","leftarrowtail":"↢","LeftCeiling":"⌈","LeftDoubleBracket":"⟦","LeftDownTeeVector":"⥡","LeftDownVectorBar":"⥙","LeftDownVector":"⇃","LeftFloor":"⌊","leftharpoondown":"↽","leftharpoonup":"↼","leftleftarrows":"⇇","leftrightarrow":"↔","LeftRightArrow":"↔","Leftrightarrow":"⇔","leftrightarrows":"⇆","leftrightharpoons":"⇋","leftrightsquigarrow":"↭","LeftRightVector":"⥎","LeftTeeArrow":"↤","LeftTee":"⊣","LeftTeeVector":"⥚","leftthreetimes":"⋋","LeftTriangleBar":"⧏","LeftTriangle":"⊲","LeftTriangleEqual":"⊴","LeftUpDownVector":"⥑","LeftUpTeeVector":"⥠","LeftUpVectorBar":"⥘","LeftUpVector":"↿","LeftVectorBar":"⥒","LeftVector":"↼","lEg":"⪋","leg":"⋚","leq":"≤","leqq":"≦","leqslant":"⩽","lescc":"⪨","les":"⩽","lesdot":"⩿","lesdoto":"⪁","lesdotor":"⪃","lesg":"⋚︀","lesges":"⪓","lessapprox":"⪅","lessdot":"⋖","lesseqgtr":"⋚","lesseqqgtr":"⪋","LessEqualGreater":"⋚","LessFullEqual":"≦","LessGreater":"≶","lessgtr":"≶","LessLess":"⪡","lesssim":"≲","LessSlantEqual":"⩽","LessTilde":"≲","lfisht":"⥼","lfloor":"⌊","Lfr":"𝔏","lfr":"𝔩","lg":"≶","lgE":"⪑","lHar":"⥢","lhard":"↽","lharu":"↼","lharul":"⥪","lhblk":"▄","LJcy":"Љ","ljcy":"љ","llarr":"⇇","ll":"≪","Ll":"⋘","llcorner":"⌞","Lleftarrow":"⇚","llhard":"⥫","lltri":"◺","Lmidot":"Ŀ","lmidot":"ŀ","lmoustache":"⎰","lmoust":"⎰","lnap":"⪉","lnapprox":"⪉","lne":"⪇","lnE":"≨","lneq":"⪇","lneqq":"≨","lnsim":"⋦","loang":"⟬","loarr":"⇽","lobrk":"⟦","longleftarrow":"⟵","LongLeftArrow":"⟵","Longleftarrow":"⟸","longleftrightarrow":"⟷","LongLeftRightArrow":"⟷","Longleftrightarrow":"⟺","longmapsto":"⟼","longrightarrow":"⟶","LongRightArrow":"⟶","Longrightarrow":"⟹","looparrowleft":"↫","looparrowright":"↬","lopar":"⦅","Lopf":"𝕃","lopf":"𝕝","loplus":"⨭","lotimes":"⨴","lowast":"∗","lowbar":"_","LowerLeftArrow":"↙","LowerRightArrow":"↘","loz":"◊","lozenge":"◊","lozf":"⧫","lpar":"(","lparlt":"⦓","lrarr":"⇆","lrcorner":"⌟","lrhar":"⇋","lrhard":"⥭","lrm":"‎","lrtri":"⊿","lsaquo":"‹","lscr":"𝓁","Lscr":"ℒ","lsh":"↰","Lsh":"↰","lsim":"≲","lsime":"⪍","lsimg":"⪏","lsqb":"[","lsquo":"‘","lsquor":"‚","Lstrok":"Ł","lstrok":"ł","ltcc":"⪦","ltcir":"⩹","lt":"<","LT":"<","Lt":"≪","ltdot":"⋖","lthree":"⋋","ltimes":"⋉","ltlarr":"⥶","ltquest":"⩻","ltri":"◃","ltrie":"⊴","ltrif":"◂","ltrPar":"⦖","lurdshar":"⥊","luruhar":"⥦","lvertneqq":"≨︀","lvnE":"≨︀","macr":"¯","male":"♂","malt":"✠","maltese":"✠","Map":"⤅","map":"↦","mapsto":"↦","mapstodown":"↧","mapstoleft":"↤","mapstoup":"↥","marker":"▮","mcomma":"⨩","Mcy":"М","mcy":"м","mdash":"—","mDDot":"∺","measuredangle":"∡","MediumSpace":" ","Mellintrf":"ℳ","Mfr":"𝔐","mfr":"𝔪","mho":"℧","micro":"µ","midast":"*","midcir":"⫰","mid":"∣","middot":"·","minusb":"⊟","minus":"−","minusd":"∸","minusdu":"⨪","MinusPlus":"∓","mlcp":"⫛","mldr":"…","mnplus":"∓","models":"⊧","Mopf":"𝕄","mopf":"𝕞","mp":"∓","mscr":"𝓂","Mscr":"ℳ","mstpos":"∾","Mu":"Μ","mu":"μ","multimap":"⊸","mumap":"⊸","nabla":"∇","Nacute":"Ń","nacute":"ń","nang":"∠⃒","nap":"≉","napE":"⩰̸","napid":"≋̸","napos":"ʼn","napprox":"≉","natural":"♮","naturals":"ℕ","natur":"♮","nbsp":" ","nbump":"≎̸","nbumpe":"≏̸","ncap":"⩃","Ncaron":"Ň","ncaron":"ň","Ncedil":"Ņ","ncedil":"ņ","ncong":"≇","ncongdot":"⩭̸","ncup":"⩂","Ncy":"Н","ncy":"н","ndash":"–","nearhk":"⤤","nearr":"↗","neArr":"⇗","nearrow":"↗","ne":"≠","nedot":"≐̸","NegativeMediumSpace":"​","NegativeThickSpace":"​","NegativeThinSpace":"​","NegativeVeryThinSpace":"​","nequiv":"≢","nesear":"⤨","nesim":"≂̸","NestedGreaterGreater":"≫","NestedLessLess":"≪","NewLine":"\\n","nexist":"∄","nexists":"∄","Nfr":"𝔑","nfr":"𝔫","ngE":"≧̸","nge":"≱","ngeq":"≱","ngeqq":"≧̸","ngeqslant":"⩾̸","nges":"⩾̸","nGg":"⋙̸","ngsim":"≵","nGt":"≫⃒","ngt":"≯","ngtr":"≯","nGtv":"≫̸","nharr":"↮","nhArr":"⇎","nhpar":"⫲","ni":"∋","nis":"⋼","nisd":"⋺","niv":"∋","NJcy":"Њ","njcy":"њ","nlarr":"↚","nlArr":"⇍","nldr":"‥","nlE":"≦̸","nle":"≰","nleftarrow":"↚","nLeftarrow":"⇍","nleftrightarrow":"↮","nLeftrightarrow":"⇎","nleq":"≰","nleqq":"≦̸","nleqslant":"⩽̸","nles":"⩽̸","nless":"≮","nLl":"⋘̸","nlsim":"≴","nLt":"≪⃒","nlt":"≮","nltri":"⋪","nltrie":"⋬","nLtv":"≪̸","nmid":"∤","NoBreak":"⁠","NonBreakingSpace":" ","nopf":"𝕟","Nopf":"ℕ","Not":"⫬","not":"¬","NotCongruent":"≢","NotCupCap":"≭","NotDoubleVerticalBar":"∦","NotElement":"∉","NotEqual":"≠","NotEqualTilde":"≂̸","NotExists":"∄","NotGreater":"≯","NotGreaterEqual":"≱","NotGreaterFullEqual":"≧̸","NotGreaterGreater":"≫̸","NotGreaterLess":"≹","NotGreaterSlantEqual":"⩾̸","NotGreaterTilde":"≵","NotHumpDownHump":"≎̸","NotHumpEqual":"≏̸","notin":"∉","notindot":"⋵̸","notinE":"⋹̸","notinva":"∉","notinvb":"⋷","notinvc":"⋶","NotLeftTriangleBar":"⧏̸","NotLeftTriangle":"⋪","NotLeftTriangleEqual":"⋬","NotLess":"≮","NotLessEqual":"≰","NotLessGreater":"≸","NotLessLess":"≪̸","NotLessSlantEqual":"⩽̸","NotLessTilde":"≴","NotNestedGreaterGreater":"⪢̸","NotNestedLessLess":"⪡̸","notni":"∌","notniva":"∌","notnivb":"⋾","notnivc":"⋽","NotPrecedes":"⊀","NotPrecedesEqual":"⪯̸","NotPrecedesSlantEqual":"⋠","NotReverseElement":"∌","NotRightTriangleBar":"⧐̸","NotRightTriangle":"⋫","NotRightTriangleEqual":"⋭","NotSquareSubset":"⊏̸","NotSquareSubsetEqual":"⋢","NotSquareSuperset":"⊐̸","NotSquareSupersetEqual":"⋣","NotSubset":"⊂⃒","NotSubsetEqual":"⊈","NotSucceeds":"⊁","NotSucceedsEqual":"⪰̸","NotSucceedsSlantEqual":"⋡","NotSucceedsTilde":"≿̸","NotSuperset":"⊃⃒","NotSupersetEqual":"⊉","NotTilde":"≁","NotTildeEqual":"≄","NotTildeFullEqual":"≇","NotTildeTilde":"≉","NotVerticalBar":"∤","nparallel":"∦","npar":"∦","nparsl":"⫽⃥","npart":"∂̸","npolint":"⨔","npr":"⊀","nprcue":"⋠","nprec":"⊀","npreceq":"⪯̸","npre":"⪯̸","nrarrc":"⤳̸","nrarr":"↛","nrArr":"⇏","nrarrw":"↝̸","nrightarrow":"↛","nRightarrow":"⇏","nrtri":"⋫","nrtrie":"⋭","nsc":"⊁","nsccue":"⋡","nsce":"⪰̸","Nscr":"𝒩","nscr":"𝓃","nshortmid":"∤","nshortparallel":"∦","nsim":"≁","nsime":"≄","nsimeq":"≄","nsmid":"∤","nspar":"∦","nsqsube":"⋢","nsqsupe":"⋣","nsub":"⊄","nsubE":"⫅̸","nsube":"⊈","nsubset":"⊂⃒","nsubseteq":"⊈","nsubseteqq":"⫅̸","nsucc":"⊁","nsucceq":"⪰̸","nsup":"⊅","nsupE":"⫆̸","nsupe":"⊉","nsupset":"⊃⃒","nsupseteq":"⊉","nsupseteqq":"⫆̸","ntgl":"≹","Ntilde":"Ñ","ntilde":"ñ","ntlg":"≸","ntriangleleft":"⋪","ntrianglelefteq":"⋬","ntriangleright":"⋫","ntrianglerighteq":"⋭","Nu":"Ν","nu":"ν","num":"#","numero":"№","numsp":" ","nvap":"≍⃒","nvdash":"⊬","nvDash":"⊭","nVdash":"⊮","nVDash":"⊯","nvge":"≥⃒","nvgt":">⃒","nvHarr":"⤄","nvinfin":"⧞","nvlArr":"⤂","nvle":"≤⃒","nvlt":"<⃒","nvltrie":"⊴⃒","nvrArr":"⤃","nvrtrie":"⊵⃒","nvsim":"∼⃒","nwarhk":"⤣","nwarr":"↖","nwArr":"⇖","nwarrow":"↖","nwnear":"⤧","Oacute":"Ó","oacute":"ó","oast":"⊛","Ocirc":"Ô","ocirc":"ô","ocir":"⊚","Ocy":"О","ocy":"о","odash":"⊝","Odblac":"Ő","odblac":"ő","odiv":"⨸","odot":"⊙","odsold":"⦼","OElig":"Œ","oelig":"œ","ofcir":"⦿","Ofr":"𝔒","ofr":"𝔬","ogon":"˛","Ograve":"Ò","ograve":"ò","ogt":"⧁","ohbar":"⦵","ohm":"Ω","oint":"∮","olarr":"↺","olcir":"⦾","olcross":"⦻","oline":"‾","olt":"⧀","Omacr":"Ō","omacr":"ō","Omega":"Ω","omega":"ω","Omicron":"Ο","omicron":"ο","omid":"⦶","ominus":"⊖","Oopf":"𝕆","oopf":"𝕠","opar":"⦷","OpenCurlyDoubleQuote":"“","OpenCurlyQuote":"‘","operp":"⦹","oplus":"⊕","orarr":"↻","Or":"⩔","or":"∨","ord":"⩝","order":"ℴ","orderof":"ℴ","ordf":"ª","ordm":"º","origof":"⊶","oror":"⩖","orslope":"⩗","orv":"⩛","oS":"Ⓢ","Oscr":"𝒪","oscr":"ℴ","Oslash":"Ø","oslash":"ø","osol":"⊘","Otilde":"Õ","otilde":"õ","otimesas":"⨶","Otimes":"⨷","otimes":"⊗","Ouml":"Ö","ouml":"ö","ovbar":"⌽","OverBar":"‾","OverBrace":"⏞","OverBracket":"⎴","OverParenthesis":"⏜","para":"¶","parallel":"∥","par":"∥","parsim":"⫳","parsl":"⫽","part":"∂","PartialD":"∂","Pcy":"П","pcy":"п","percnt":"%","period":".","permil":"‰","perp":"⊥","pertenk":"‱","Pfr":"𝔓","pfr":"𝔭","Phi":"Φ","phi":"φ","phiv":"ϕ","phmmat":"ℳ","phone":"☎","Pi":"Π","pi":"π","pitchfork":"⋔","piv":"ϖ","planck":"ℏ","planckh":"ℎ","plankv":"ℏ","plusacir":"⨣","plusb":"⊞","pluscir":"⨢","plus":"+","plusdo":"∔","plusdu":"⨥","pluse":"⩲","PlusMinus":"±","plusmn":"±","plussim":"⨦","plustwo":"⨧","pm":"±","Poincareplane":"ℌ","pointint":"⨕","popf":"𝕡","Popf":"ℙ","pound":"£","prap":"⪷","Pr":"⪻","pr":"≺","prcue":"≼","precapprox":"⪷","prec":"≺","preccurlyeq":"≼","Precedes":"≺","PrecedesEqual":"⪯","PrecedesSlantEqual":"≼","PrecedesTilde":"≾","preceq":"⪯","precnapprox":"⪹","precneqq":"⪵","precnsim":"⋨","pre":"⪯","prE":"⪳","precsim":"≾","prime":"′","Prime":"″","primes":"ℙ","prnap":"⪹","prnE":"⪵","prnsim":"⋨","prod":"∏","Product":"∏","profalar":"⌮","profline":"⌒","profsurf":"⌓","prop":"∝","Proportional":"∝","Proportion":"∷","propto":"∝","prsim":"≾","prurel":"⊰","Pscr":"𝒫","pscr":"𝓅","Psi":"Ψ","psi":"ψ","puncsp":" ","Qfr":"𝔔","qfr":"𝔮","qint":"⨌","qopf":"𝕢","Qopf":"ℚ","qprime":"⁗","Qscr":"𝒬","qscr":"𝓆","quaternions":"ℍ","quatint":"⨖","quest":"?","questeq":"≟","quot":"\\"","QUOT":"\\"","rAarr":"⇛","race":"∽̱","Racute":"Ŕ","racute":"ŕ","radic":"√","raemptyv":"⦳","rang":"⟩","Rang":"⟫","rangd":"⦒","range":"⦥","rangle":"⟩","raquo":"»","rarrap":"⥵","rarrb":"⇥","rarrbfs":"⤠","rarrc":"⤳","rarr":"→","Rarr":"↠","rArr":"⇒","rarrfs":"⤞","rarrhk":"↪","rarrlp":"↬","rarrpl":"⥅","rarrsim":"⥴","Rarrtl":"⤖","rarrtl":"↣","rarrw":"↝","ratail":"⤚","rAtail":"⤜","ratio":"∶","rationals":"ℚ","rbarr":"⤍","rBarr":"⤏","RBarr":"⤐","rbbrk":"❳","rbrace":"}","rbrack":"]","rbrke":"⦌","rbrksld":"⦎","rbrkslu":"⦐","Rcaron":"Ř","rcaron":"ř","Rcedil":"Ŗ","rcedil":"ŗ","rceil":"⌉","rcub":"}","Rcy":"Р","rcy":"р","rdca":"⤷","rdldhar":"⥩","rdquo":"”","rdquor":"”","rdsh":"↳","real":"ℜ","realine":"ℛ","realpart":"ℜ","reals":"ℝ","Re":"ℜ","rect":"▭","reg":"®","REG":"®","ReverseElement":"∋","ReverseEquilibrium":"⇋","ReverseUpEquilibrium":"⥯","rfisht":"⥽","rfloor":"⌋","rfr":"𝔯","Rfr":"ℜ","rHar":"⥤","rhard":"⇁","rharu":"⇀","rharul":"⥬","Rho":"Ρ","rho":"ρ","rhov":"ϱ","RightAngleBracket":"⟩","RightArrowBar":"⇥","rightarrow":"→","RightArrow":"→","Rightarrow":"⇒","RightArrowLeftArrow":"⇄","rightarrowtail":"↣","RightCeiling":"⌉","RightDoubleBracket":"⟧","RightDownTeeVector":"⥝","RightDownVectorBar":"⥕","RightDownVector":"⇂","RightFloor":"⌋","rightharpoondown":"⇁","rightharpoonup":"⇀","rightleftarrows":"⇄","rightleftharpoons":"⇌","rightrightarrows":"⇉","rightsquigarrow":"↝","RightTeeArrow":"↦","RightTee":"⊢","RightTeeVector":"⥛","rightthreetimes":"⋌","RightTriangleBar":"⧐","RightTriangle":"⊳","RightTriangleEqual":"⊵","RightUpDownVector":"⥏","RightUpTeeVector":"⥜","RightUpVectorBar":"⥔","RightUpVector":"↾","RightVectorBar":"⥓","RightVector":"⇀","ring":"˚","risingdotseq":"≓","rlarr":"⇄","rlhar":"⇌","rlm":"‏","rmoustache":"⎱","rmoust":"⎱","rnmid":"⫮","roang":"⟭","roarr":"⇾","robrk":"⟧","ropar":"⦆","ropf":"𝕣","Ropf":"ℝ","roplus":"⨮","rotimes":"⨵","RoundImplies":"⥰","rpar":")","rpargt":"⦔","rppolint":"⨒","rrarr":"⇉","Rrightarrow":"⇛","rsaquo":"›","rscr":"𝓇","Rscr":"ℛ","rsh":"↱","Rsh":"↱","rsqb":"]","rsquo":"’","rsquor":"’","rthree":"⋌","rtimes":"⋊","rtri":"▹","rtrie":"⊵","rtrif":"▸","rtriltri":"⧎","RuleDelayed":"⧴","ruluhar":"⥨","rx":"℞","Sacute":"Ś","sacute":"ś","sbquo":"‚","scap":"⪸","Scaron":"Š","scaron":"š","Sc":"⪼","sc":"≻","sccue":"≽","sce":"⪰","scE":"⪴","Scedil":"Ş","scedil":"ş","Scirc":"Ŝ","scirc":"ŝ","scnap":"⪺","scnE":"⪶","scnsim":"⋩","scpolint":"⨓","scsim":"≿","Scy":"С","scy":"с","sdotb":"⊡","sdot":"⋅","sdote":"⩦","searhk":"⤥","searr":"↘","seArr":"⇘","searrow":"↘","sect":"§","semi":";","seswar":"⤩","setminus":"∖","setmn":"∖","sext":"✶","Sfr":"𝔖","sfr":"𝔰","sfrown":"⌢","sharp":"♯","SHCHcy":"Щ","shchcy":"щ","SHcy":"Ш","shcy":"ш","ShortDownArrow":"↓","ShortLeftArrow":"←","shortmid":"∣","shortparallel":"∥","ShortRightArrow":"→","ShortUpArrow":"↑","shy":"­","Sigma":"Σ","sigma":"σ","sigmaf":"ς","sigmav":"ς","sim":"∼","simdot":"⩪","sime":"≃","simeq":"≃","simg":"⪞","simgE":"⪠","siml":"⪝","simlE":"⪟","simne":"≆","simplus":"⨤","simrarr":"⥲","slarr":"←","SmallCircle":"∘","smallsetminus":"∖","smashp":"⨳","smeparsl":"⧤","smid":"∣","smile":"⌣","smt":"⪪","smte":"⪬","smtes":"⪬︀","SOFTcy":"Ь","softcy":"ь","solbar":"⌿","solb":"⧄","sol":"/","Sopf":"𝕊","sopf":"𝕤","spades":"♠","spadesuit":"♠","spar":"∥","sqcap":"⊓","sqcaps":"⊓︀","sqcup":"⊔","sqcups":"⊔︀","Sqrt":"√","sqsub":"⊏","sqsube":"⊑","sqsubset":"⊏","sqsubseteq":"⊑","sqsup":"⊐","sqsupe":"⊒","sqsupset":"⊐","sqsupseteq":"⊒","square":"□","Square":"□","SquareIntersection":"⊓","SquareSubset":"⊏","SquareSubsetEqual":"⊑","SquareSuperset":"⊐","SquareSupersetEqual":"⊒","SquareUnion":"⊔","squarf":"▪","squ":"□","squf":"▪","srarr":"→","Sscr":"𝒮","sscr":"𝓈","ssetmn":"∖","ssmile":"⌣","sstarf":"⋆","Star":"⋆","star":"☆","starf":"★","straightepsilon":"ϵ","straightphi":"ϕ","strns":"¯","sub":"⊂","Sub":"⋐","subdot":"⪽","subE":"⫅","sube":"⊆","subedot":"⫃","submult":"⫁","subnE":"⫋","subne":"⊊","subplus":"⪿","subrarr":"⥹","subset":"⊂","Subset":"⋐","subseteq":"⊆","subseteqq":"⫅","SubsetEqual":"⊆","subsetneq":"⊊","subsetneqq":"⫋","subsim":"⫇","subsub":"⫕","subsup":"⫓","succapprox":"⪸","succ":"≻","succcurlyeq":"≽","Succeeds":"≻","SucceedsEqual":"⪰","SucceedsSlantEqual":"≽","SucceedsTilde":"≿","succeq":"⪰","succnapprox":"⪺","succneqq":"⪶","succnsim":"⋩","succsim":"≿","SuchThat":"∋","sum":"∑","Sum":"∑","sung":"♪","sup1":"¹","sup2":"²","sup3":"³","sup":"⊃","Sup":"⋑","supdot":"⪾","supdsub":"⫘","supE":"⫆","supe":"⊇","supedot":"⫄","Superset":"⊃","SupersetEqual":"⊇","suphsol":"⟉","suphsub":"⫗","suplarr":"⥻","supmult":"⫂","supnE":"⫌","supne":"⊋","supplus":"⫀","supset":"⊃","Supset":"⋑","supseteq":"⊇","supseteqq":"⫆","supsetneq":"⊋","supsetneqq":"⫌","supsim":"⫈","supsub":"⫔","supsup":"⫖","swarhk":"⤦","swarr":"↙","swArr":"⇙","swarrow":"↙","swnwar":"⤪","szlig":"ß","Tab":"\\t","target":"⌖","Tau":"Τ","tau":"τ","tbrk":"⎴","Tcaron":"Ť","tcaron":"ť","Tcedil":"Ţ","tcedil":"ţ","Tcy":"Т","tcy":"т","tdot":"⃛","telrec":"⌕","Tfr":"𝔗","tfr":"𝔱","there4":"∴","therefore":"∴","Therefore":"∴","Theta":"Θ","theta":"θ","thetasym":"ϑ","thetav":"ϑ","thickapprox":"≈","thicksim":"∼","ThickSpace":"  ","ThinSpace":" ","thinsp":" ","thkap":"≈","thksim":"∼","THORN":"Þ","thorn":"þ","tilde":"˜","Tilde":"∼","TildeEqual":"≃","TildeFullEqual":"≅","TildeTilde":"≈","timesbar":"⨱","timesb":"⊠","times":"×","timesd":"⨰","tint":"∭","toea":"⤨","topbot":"⌶","topcir":"⫱","top":"⊤","Topf":"𝕋","topf":"𝕥","topfork":"⫚","tosa":"⤩","tprime":"‴","trade":"™","TRADE":"™","triangle":"▵","triangledown":"▿","triangleleft":"◃","trianglelefteq":"⊴","triangleq":"≜","triangleright":"▹","trianglerighteq":"⊵","tridot":"◬","trie":"≜","triminus":"⨺","TripleDot":"⃛","triplus":"⨹","trisb":"⧍","tritime":"⨻","trpezium":"⏢","Tscr":"𝒯","tscr":"𝓉","TScy":"Ц","tscy":"ц","TSHcy":"Ћ","tshcy":"ћ","Tstrok":"Ŧ","tstrok":"ŧ","twixt":"≬","twoheadleftarrow":"↞","twoheadrightarrow":"↠","Uacute":"Ú","uacute":"ú","uarr":"↑","Uarr":"↟","uArr":"⇑","Uarrocir":"⥉","Ubrcy":"Ў","ubrcy":"ў","Ubreve":"Ŭ","ubreve":"ŭ","Ucirc":"Û","ucirc":"û","Ucy":"У","ucy":"у","udarr":"⇅","Udblac":"Ű","udblac":"ű","udhar":"⥮","ufisht":"⥾","Ufr":"𝔘","ufr":"𝔲","Ugrave":"Ù","ugrave":"ù","uHar":"⥣","uharl":"↿","uharr":"↾","uhblk":"▀","ulcorn":"⌜","ulcorner":"⌜","ulcrop":"⌏","ultri":"◸","Umacr":"Ū","umacr":"ū","uml":"¨","UnderBar":"_","UnderBrace":"⏟","UnderBracket":"⎵","UnderParenthesis":"⏝","Union":"⋃","UnionPlus":"⊎","Uogon":"Ų","uogon":"ų","Uopf":"𝕌","uopf":"𝕦","UpArrowBar":"⤒","uparrow":"↑","UpArrow":"↑","Uparrow":"⇑","UpArrowDownArrow":"⇅","updownarrow":"↕","UpDownArrow":"↕","Updownarrow":"⇕","UpEquilibrium":"⥮","upharpoonleft":"↿","upharpoonright":"↾","uplus":"⊎","UpperLeftArrow":"↖","UpperRightArrow":"↗","upsi":"υ","Upsi":"ϒ","upsih":"ϒ","Upsilon":"Υ","upsilon":"υ","UpTeeArrow":"↥","UpTee":"⊥","upuparrows":"⇈","urcorn":"⌝","urcorner":"⌝","urcrop":"⌎","Uring":"Ů","uring":"ů","urtri":"◹","Uscr":"𝒰","uscr":"𝓊","utdot":"⋰","Utilde":"Ũ","utilde":"ũ","utri":"▵","utrif":"▴","uuarr":"⇈","Uuml":"Ü","uuml":"ü","uwangle":"⦧","vangrt":"⦜","varepsilon":"ϵ","varkappa":"ϰ","varnothing":"∅","varphi":"ϕ","varpi":"ϖ","varpropto":"∝","varr":"↕","vArr":"⇕","varrho":"ϱ","varsigma":"ς","varsubsetneq":"⊊︀","varsubsetneqq":"⫋︀","varsupsetneq":"⊋︀","varsupsetneqq":"⫌︀","vartheta":"ϑ","vartriangleleft":"⊲","vartriangleright":"⊳","vBar":"⫨","Vbar":"⫫","vBarv":"⫩","Vcy":"В","vcy":"в","vdash":"⊢","vDash":"⊨","Vdash":"⊩","VDash":"⊫","Vdashl":"⫦","veebar":"⊻","vee":"∨","Vee":"⋁","veeeq":"≚","vellip":"⋮","verbar":"|","Verbar":"‖","vert":"|","Vert":"‖","VerticalBar":"∣","VerticalLine":"|","VerticalSeparator":"❘","VerticalTilde":"≀","VeryThinSpace":" ","Vfr":"𝔙","vfr":"𝔳","vltri":"⊲","vnsub":"⊂⃒","vnsup":"⊃⃒","Vopf":"𝕍","vopf":"𝕧","vprop":"∝","vrtri":"⊳","Vscr":"𝒱","vscr":"𝓋","vsubnE":"⫋︀","vsubne":"⊊︀","vsupnE":"⫌︀","vsupne":"⊋︀","Vvdash":"⊪","vzigzag":"⦚","Wcirc":"Ŵ","wcirc":"ŵ","wedbar":"⩟","wedge":"∧","Wedge":"⋀","wedgeq":"≙","weierp":"℘","Wfr":"𝔚","wfr":"𝔴","Wopf":"𝕎","wopf":"𝕨","wp":"℘","wr":"≀","wreath":"≀","Wscr":"𝒲","wscr":"𝓌","xcap":"⋂","xcirc":"◯","xcup":"⋃","xdtri":"▽","Xfr":"𝔛","xfr":"𝔵","xharr":"⟷","xhArr":"⟺","Xi":"Ξ","xi":"ξ","xlarr":"⟵","xlArr":"⟸","xmap":"⟼","xnis":"⋻","xodot":"⨀","Xopf":"𝕏","xopf":"𝕩","xoplus":"⨁","xotime":"⨂","xrarr":"⟶","xrArr":"⟹","Xscr":"𝒳","xscr":"𝓍","xsqcup":"⨆","xuplus":"⨄","xutri":"△","xvee":"⋁","xwedge":"⋀","Yacute":"Ý","yacute":"ý","YAcy":"Я","yacy":"я","Ycirc":"Ŷ","ycirc":"ŷ","Ycy":"Ы","ycy":"ы","yen":"¥","Yfr":"𝔜","yfr":"𝔶","YIcy":"Ї","yicy":"ї","Yopf":"𝕐","yopf":"𝕪","Yscr":"𝒴","yscr":"𝓎","YUcy":"Ю","yucy":"ю","yuml":"ÿ","Yuml":"Ÿ","Zacute":"Ź","zacute":"ź","Zcaron":"Ž","zcaron":"ž","Zcy":"З","zcy":"з","Zdot":"Ż","zdot":"ż","zeetrf":"ℨ","ZeroWidthSpace":"​","Zeta":"Ζ","zeta":"ζ","zfr":"𝔷","Zfr":"ℨ","ZHcy":"Ж","zhcy":"ж","zigrarr":"⇝","zopf":"𝕫","Zopf":"ℤ","Zscr":"𝒵","zscr":"𝓏","zwj":"‍","zwnj":"‌"}'); - /***/ }) /******/ }); diff --git a/netbox/project-static/dist/graphiql/index.umd.js b/netbox/project-static/dist/graphiql/index.umd.js index a30153f93..5089eab84 100644 --- a/netbox/project-static/dist/graphiql/index.umd.js +++ b/netbox/project-static/dist/graphiql/index.umd.js @@ -1 +1 @@ -(function(z,W){typeof exports=="object"&&typeof module<"u"?W(exports,require("@graphiql/react"),require("react"),require("graphql")):typeof define=="function"&&define.amd?define(["exports","@graphiql/react","react","graphql"],W):(z=typeof globalThis<"u"?globalThis:z||self,W(z.GraphiQLPluginExplorer={},z.GraphiQL.React,z.React,z.GraphiQL.GraphQL))})(this,function(z,W,N,De){"use strict";function ve(i){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(i){for(const s in i)if(s!=="default"){const n=Object.getOwnPropertyDescriptor(i,s);Object.defineProperty(t,s,n.get?n:{enumerable:!0,get:()=>i[s]})}}return t.default=i,Object.freeze(t)}const Te=ve(N),Ne=ve(De);function ge(i){return i&&Object.prototype.hasOwnProperty.call(i,"default")&&Object.keys(i).length===1?i.default:i}var re={},ie={};const Le=ge(Te),Me=ge(Ne);Object.defineProperty(ie,"__esModule",{value:!0});var je=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(i){return typeof i}:function(i){return i&&typeof Symbol=="function"&&i.constructor===Symbol&&i!==Symbol.prototype?"symbol":typeof i},ye=function(){function i(t,s){var n=[],e=!0,l=!1,c=void 0;try{for(var f=t[Symbol.iterator](),u;!(e=(u=f.next()).done)&&(n.push(u.value),!(s&&n.length===s));e=!0);}catch(r){l=!0,c=r}finally{try{!e&&f.return&&f.return()}finally{if(l)throw c}}return n}return function(t,s){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return i(t,s);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),k=Object.assign||function(i){for(var t=1;t"u"?p=!0:typeof r.kind=="string"&&(h=!0)}catch{}var F=e.props.selection,d=e._getArgSelection();if(!d){console.error("missing arg selection when setting arg value");return}var b=Q(e.props.arg.type),g=(0,v.isLeafType)(b)||m||p||h;if(!g){console.warn("Unable to handle non leaf types in InputArgView.setArgValue",r);return}var C=void 0,_=void 0;r===null||typeof r>"u"?_=null:!r.target&&r.kind&&r.kind==="VariableDefinition"?(C=r,_=C.variable):typeof r.kind=="string"?_=r:r.target&&typeof r.target.value=="string"&&(C=r.target.value,_=ke(b,C));var A=e.props.modifyFields((F.fields||[]).map(function(D){var B=D===d,j=B?k({},D,{value:_}):D;return j}),o);return A},e._modifyChildFields=function(r){return e.props.modifyFields(e.props.selection.fields.map(function(o){return o.name.value===e.props.arg.name?k({},o,{value:{kind:"ObjectValue",fields:r}}):o}),!0)},n),w(e,l)}return P(t,[{key:"render",value:function(){var n=this.props,e=n.arg,l=n.parentField,c=this._getArgSelection();return a.createElement(Ee,{argValue:c?c.value:null,arg:e,parentField:l,addArg:this._addArg,removeArg:this._removeArg,setArgFields:this._modifyChildFields,setArgValue:this._setArgValue,getDefaultScalarArgValue:this.props.getDefaultScalarArgValue,makeDefaultArg:this.props.makeDefaultArg,onRunOperation:this.props.onRunOperation,styleConfig:this.props.styleConfig,onCommit:this.props.onCommit,definition:this.props.definition})}}]),t}(a.PureComponent);function ue(i){if((0,v.isEnumType)(i))return{kind:"EnumValue",value:i.getValues()[0].name};switch(i.name){case"String":return{kind:"StringValue",value:""};case"Float":return{kind:"FloatValue",value:"1.5"};case"Int":return{kind:"IntValue",value:"10"};case"Boolean":return{kind:"BooleanValue",value:!1};default:return{kind:"StringValue",value:""}}}function Ce(i,t,s){return ue(s)}var We=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u"u"?p=!0:typeof r.kind=="string"&&(h=!0)}catch{}var F=e.props.selection,d=e._getArgSelection();if(!d&&!m){console.error("missing arg selection when setting arg value");return}var b=Q(e.props.arg.type),g=(0,v.isLeafType)(b)||m||p||h;if(!g){console.warn("Unable to handle non leaf types in ArgView._setArgValue");return}var C=void 0,_=void 0;return r===null||typeof r>"u"?_=null:r.target&&typeof r.target.value=="string"?(C=r.target.value,_=ke(b,C)):!r.target&&r.kind==="VariableDefinition"?(C=r,_=C.variable):typeof r.kind=="string"&&(_=r),e.props.modifyArguments((F.arguments||[]).map(function(A){return A===d?k({},A,{value:_}):A}),o)},e._setArgFields=function(r,o){var p=e.props.selection,m=e._getArgSelection();if(!m){console.error("missing arg selection when setting arg value");return}return e.props.modifyArguments((p.arguments||[]).map(function(h){return h===m?k({},h,{value:{kind:"ObjectValue",fields:r}}):h}),o)},n),w(e,l)}return P(t,[{key:"render",value:function(){var n=this.props,e=n.arg,l=n.parentField,c=this._getArgSelection();return a.createElement(Ee,{argValue:c?c.value:null,arg:e,parentField:l,addArg:this._addArg,removeArg:this._removeArg,setArgFields:this._setArgFields,setArgValue:this._setArgValue,getDefaultScalarArgValue:this.props.getDefaultScalarArgValue,makeDefaultArg:this.props.makeDefaultArg,onRunOperation:this.props.onRunOperation,styleConfig:this.props.styleConfig,onCommit:this.props.onCommit,definition:this.props.definition})}}]),t}(a.PureComponent);function Qe(i){return i.ctrlKey&&i.key==="Enter"}function Ze(i){return i!=="FragmentDefinition"}var Ke=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u0?C=""+b+g:C=b;var _=c.type.toString(),A=(0,v.parseType)(_),D={kind:"VariableDefinition",variable:{kind:"Variable",name:{kind:"Name",value:C}},type:A,directives:[]},B=function(x){return(n.props.definition.variableDefinitions||[]).find(function(T){return T.variable.name.value===x})},j=void 0,G={};if(typeof l<"u"&&l!==null){var U=(0,v.visit)(l,{Variable:function(x){var T=x.name.value,J=B(T);if(G[T]=G[T]+1||1,!!J)return J.defaultValue}}),K=D.type.kind==="NonNullType",M=K?k({},D,{type:D.type.type}):D;j=k({},M,{defaultValue:U})}else j=D;var $=Object.entries(G).filter(function(E){var x=ye(E,2);x[0];var T=x[1];return T<2}).map(function(E){var x=ye(E,2),T=x[0];return x[1],T});if(j){var X=n.props.setArgValue(j,!1);if(X){var ee=X.definitions.find(function(E){return E.operation&&E.name&&E.name.value&&n.props.definition.name&&n.props.definition.name.value?E.name.value===n.props.definition.name.value:!1}),y=[].concat(I(ee.variableDefinitions||[]),[j]).filter(function(E){return $.indexOf(E.variable.name.value)===-1}),S=k({},ee,{variableDefinitions:y}),O=X.definitions,V=O.map(function(E){return ee===E?S:E}),L=k({},X,{definitions:V});n.props.onCommit(L)}}},m=function(){if(!(!l||!l.name||!l.name.value)){var b=l.name.value,g=(n.props.definition.variableDefinitions||[]).find(function(M){return M.variable.name.value===b});if(g){var C=g.defaultValue,_=n.props.setArgValue(C,{commit:!1});if(_){var A=_.definitions.find(function(M){return M.name.value===n.props.definition.name.value});if(!A)return;var D=0;(0,v.visit)(A,{Variable:function($){$.name.value===b&&(D=D+1)}});var B=A.variableDefinitions||[];D<2&&(B=B.filter(function(M){return M.variable.name.value!==b}));var j=k({},A,{variableDefinitions:B}),G=_.definitions,U=G.map(function(M){return A===M?j:M}),K=k({},_,{definitions:U});n.props.onCommit(K)}}}},h=l&&l.kind==="Variable",F=this.state.displayArgActions?a.createElement("button",{type:"submit",className:"toolbar-button",title:h?"Remove the variable":"Extract the current value into a GraphQL variable",onClick:function(b){b.preventDefault(),b.stopPropagation(),h?m():p()},style:f.styles.actionButtonStyle},a.createElement("span",{style:{color:f.colors.variable}},"$")):null;return a.createElement("div",{style:{cursor:"pointer",minHeight:"16px",WebkitUserSelect:"none",userSelect:"none"},"data-arg-name":c.name,"data-arg-type":u.name,className:"graphiql-explorer-"+c.name},a.createElement("span",{style:{cursor:"pointer"},onClick:function(b){var g=!l;g?n.props.addArg(!0):n.props.removeArg(!0),n.setState({displayArgActions:g})}},(0,v.isInputObjectType)(u)?a.createElement("span",null,l?this.props.styleConfig.arrowOpen:this.props.styleConfig.arrowClosed):a.createElement(ae,{checked:!!l,styleConfig:this.props.styleConfig}),a.createElement("span",{style:{color:f.colors.attribute},title:c.description,onMouseEnter:function(){l!==null&&typeof l<"u"&&n.setState({displayArgActions:!0})},onMouseLeave:function(){return n.setState({displayArgActions:!1})}},c.name,Se(c)?"*":"",": ",F," ")," "),r||a.createElement("span",null)," ")}}]),t}(a.PureComponent),Je=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u0;C&&n.setState({displayFieldActions:!0})},onMouseLeave:function(){return n.setState({displayFieldActions:!1})}},(0,v.isObjectType)(o)?a.createElement("span",null,r?this.props.styleConfig.arrowOpen:this.props.styleConfig.arrowClosed):null,(0,v.isObjectType)(o)?null:a.createElement(ae,{checked:!!r,styleConfig:this.props.styleConfig}),a.createElement("span",{style:{color:u.colors.property},className:"graphiql-explorer-field-view"},l.name),this.state.displayFieldActions?a.createElement("button",{type:"submit",className:"toolbar-button",title:"Extract selections into a new reusable fragment",onClick:function(C){C.preventDefault(),C.stopPropagation();var _=o.name,A=_+"Fragment",D=(h||[]).filter(function(M){return M.name.value.startsWith(A)}).length;D>0&&(A=""+A+D);var B=r?r.selectionSet?r.selectionSet.selections:[]:[],j=[{kind:"FragmentSpread",name:{kind:"Name",value:A},directives:[]}],G={kind:"FragmentDefinition",name:{kind:"Name",value:A},typeCondition:{kind:"NamedType",name:{kind:"Name",value:o.name}},directives:[],selectionSet:{kind:"SelectionSet",selections:B}},U=n._modifyChildSelections(j,!1);if(U){var K=k({},U,{definitions:[].concat(I(U.definitions),[G])});n.props.onCommit(K)}else console.warn("Unable to complete extractFragment operation")},style:k({},u.styles.actionButtonStyle)},a.createElement("span",null,"…")):null),r&&p.length?a.createElement("div",{style:{marginLeft:16},className:"graphiql-explorer-graphql-arguments"},p.map(function(g){return a.createElement(We,{key:g.name,parentField:l,arg:g,selection:r,modifyArguments:n._setArguments,getDefaultScalarArgValue:n.props.getDefaultScalarArgValue,makeDefaultArg:n.props.makeDefaultArg,onRunOperation:n.props.onRunOperation,styleConfig:n.props.styleConfig,onCommit:n.props.onCommit,definition:n.props.definition})})):null);if(r&&((0,v.isObjectType)(o)||(0,v.isInterfaceType)(o)||(0,v.isUnionType)(o))){var d=(0,v.isUnionType)(o)?{}:o.getFields(),b=r?r.selectionSet?r.selectionSet.selections:[]:[];return a.createElement("div",{className:"graphiql-explorer-"+l.name},F,a.createElement("div",{style:{marginLeft:16}},h?h.map(function(g){var C=c.getType(g.typeCondition.name.value),_=g.name.value;return C?a.createElement(Ye,{key:_,fragment:g,selections:b,modifySelections:n._modifyChildSelections,schema:c,styleConfig:n.props.styleConfig,onCommit:n.props.onCommit}):null}):null,Object.keys(d).sort().map(function(g){return a.createElement(t,{key:g,field:d[g],selections:b,modifySelections:n._modifyChildSelections,schema:c,getDefaultFieldNames:f,getDefaultScalarArgValue:n.props.getDefaultScalarArgValue,makeDefaultArg:n.props.makeDefaultArg,onRunOperation:n.props.onRunOperation,styleConfig:n.props.styleConfig,onCommit:n.props.onCommit,definition:n.props.definition,availableFragments:n.props.availableFragments})}),(0,v.isInterfaceType)(o)||(0,v.isUnionType)(o)?c.getPossibleTypes(o).map(function(g){return a.createElement(Je,{key:g.name,implementingType:g,selections:b,modifySelections:n._modifyChildSelections,schema:c,getDefaultFieldNames:f,getDefaultScalarArgValue:n.props.getDefaultScalarArgValue,makeDefaultArg:n.props.makeDefaultArg,onRunOperation:n.props.onRunOperation,styleConfig:n.props.styleConfig,onCommit:n.props.onCommit,definition:n.props.definition})}):null))}return F}}]),t}(a.PureComponent);function Xe(i){try{return i.trim()?(0,v.parse)(i,{noLocation:!0}):null}catch(t){return new Error(t)}}var $e={kind:"OperationDefinition",operation:"query",variableDefinitions:[],name:{kind:"Name",value:"MyQuery"},directives:[],selectionSet:{kind:"SelectionSet",selections:[]}},le={kind:"Document",definitions:[$e]},Y=null;function et(i){if(Y&&Y[0]===i)return Y[1];var t=Xe(i);return t?t instanceof Error?Y?Y[1]:le:(Y=[i,t],t):le}var Oe={buttonStyle:{fontSize:"1.2em",padding:"0px",backgroundColor:"white",border:"none",margin:"5px 0px",height:"40px",width:"100%",display:"block",maxWidth:"none"},actionButtonStyle:{padding:"0px",backgroundColor:"white",border:"none",margin:"0px",maxWidth:"none",height:"15px",width:"15px",display:"inline-block",fontSize:"smaller"},explorerActionsStyle:{margin:"4px -8px -8px",paddingLeft:"8px",bottom:"0px",width:"100%",textAlign:"center",background:"none",borderTop:"none",borderBottom:"none"}},tt=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u"u"?"undefined":je(Z))==="object"&&typeof Z.commit<"u"?me=Z.commit:me=!0,H){var de=k({},d,{definitions:d.definitions.map(function(Ve){return Ve===y?H:Ve})});return me&&te(de),de}else return d},schema:l,getDefaultFieldNames:b,getDefaultScalarArgValue:g,makeDefaultArg:f,onRunOperation:function(){n.props.onRunOperation&&n.props.onRunOperation(O)},styleConfig:u,availableFragments:X})}),ee),K)}}]),t}(a.PureComponent);we.defaultProps={getDefaultFieldNames:be,getDefaultScalarArgValue:Ce};var rt=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u{f&&t(f),n()},[n,t]),[l,c]=W.useOperationsEditorState();return N.createElement(Fe,{schema:s,onRunOperation:e,explorerIsOpen:!0,colors:ot,arrowOpen:lt,arrowClosed:st,checkboxUnchecked:ut,checkboxChecked:pt,styles:ct,query:l,onEdit:c,...i})}function mt(i){return{title:"GraphiQL Explorer",icon:()=>N.createElement("svg",{height:"1em",strokeWidth:"1.5",viewBox:"0 0 24 24",fill:"none"},N.createElement("path",{d:"M18 6H20M22 6H20M20 6V4M20 6V8",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round"}),N.createElement("path",{d:"M21.4 20H2.6C2.26863 20 2 19.7314 2 19.4V11H21.4C21.7314 11 22 11.2686 22 11.6V19.4C22 19.7314 21.7314 20 21.4 20Z",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round"}),N.createElement("path",{d:"M2 11V4.6C2 4.26863 2.26863 4 2.6 4H8.77805C8.92127 4 9.05977 4.05124 9.16852 4.14445L12.3315 6.85555C12.4402 6.94876 12.5787 7 12.722 7H14",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round"})),content:()=>N.createElement(ft,{...i})}}z.explorerPlugin=mt,Object.defineProperty(z,Symbol.toStringTag,{value:"Module"})}); +(function(z,G){typeof exports=="object"&&typeof module<"u"?G(exports,require("@graphiql/react"),require("react"),require("graphql")):typeof define=="function"&&define.amd?define(["exports","@graphiql/react","react","graphql"],G):(z=typeof globalThis<"u"?globalThis:z||self,G(z.GraphiQLPluginExplorer={},z.GraphiQL.React,z.React,z.GraphiQL.GraphQL))})(this,function(z,G,N,De){"use strict";function ve(i){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(i){for(const s in i)if(s!=="default"){const n=Object.getOwnPropertyDescriptor(i,s);Object.defineProperty(t,s,n.get?n:{enumerable:!0,get:()=>i[s]})}}return t.default=i,Object.freeze(t)}const Te=ve(N),Ne=ve(De);function ge(i){return i&&Object.prototype.hasOwnProperty.call(i,"default")&&Object.keys(i).length===1?i.default:i}var re={},ie={};const Le=ge(Te),Me=ge(Ne);Object.defineProperty(ie,"__esModule",{value:!0});var je=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(i){return typeof i}:function(i){return i&&typeof Symbol=="function"&&i.constructor===Symbol&&i!==Symbol.prototype?"symbol":typeof i},ye=function(){function i(t,s){var n=[],e=!0,l=!1,c=void 0;try{for(var f=t[Symbol.iterator](),u;!(e=(u=f.next()).done)&&(n.push(u.value),!(s&&n.length===s));e=!0);}catch(r){l=!0,c=r}finally{try{!e&&f.return&&f.return()}finally{if(l)throw c}}return n}return function(t,s){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return i(t,s);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),k=Object.assign||function(i){for(var t=1;t"u"?p=!0:typeof r.kind=="string"&&(h=!0)}catch{}var F=e.props.selection,d=e._getArgSelection();if(!d){console.error("missing arg selection when setting arg value");return}var b=Q(e.props.arg.type),g=(0,v.isLeafType)(b)||m||p||h;if(!g){console.warn("Unable to handle non leaf types in InputArgView.setArgValue",r);return}var C=void 0,_=void 0;r===null||typeof r>"u"?_=null:!r.target&&r.kind&&r.kind==="VariableDefinition"?(C=r,_=C.variable):typeof r.kind=="string"?_=r:r.target&&typeof r.target.value=="string"&&(C=r.target.value,_=ke(b,C));var A=e.props.modifyFields((F.fields||[]).map(function(D){var B=D===d,j=B?k({},D,{value:_}):D;return j}),o);return A},e._modifyChildFields=function(r){return e.props.modifyFields(e.props.selection.fields.map(function(o){return o.name.value===e.props.arg.name?k({},o,{value:{kind:"ObjectValue",fields:r}}):o}),!0)},n),w(e,l)}return P(t,[{key:"render",value:function(){var n=this.props,e=n.arg,l=n.parentField,c=this._getArgSelection();return a.createElement(Ee,{argValue:c?c.value:null,arg:e,parentField:l,addArg:this._addArg,removeArg:this._removeArg,setArgFields:this._modifyChildFields,setArgValue:this._setArgValue,getDefaultScalarArgValue:this.props.getDefaultScalarArgValue,makeDefaultArg:this.props.makeDefaultArg,onRunOperation:this.props.onRunOperation,styleConfig:this.props.styleConfig,onCommit:this.props.onCommit,definition:this.props.definition})}}]),t}(a.PureComponent);function ue(i){if((0,v.isEnumType)(i))return{kind:"EnumValue",value:i.getValues()[0].name};switch(i.name){case"String":return{kind:"StringValue",value:""};case"Float":return{kind:"FloatValue",value:"1.5"};case"Int":return{kind:"IntValue",value:"10"};case"Boolean":return{kind:"BooleanValue",value:!1};default:return{kind:"StringValue",value:""}}}function Ce(i,t,s){return ue(s)}var We=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u"u"?p=!0:typeof r.kind=="string"&&(h=!0)}catch{}var F=e.props.selection,d=e._getArgSelection();if(!d&&!m){console.error("missing arg selection when setting arg value");return}var b=Q(e.props.arg.type),g=(0,v.isLeafType)(b)||m||p||h;if(!g){console.warn("Unable to handle non leaf types in ArgView._setArgValue");return}var C=void 0,_=void 0;return r===null||typeof r>"u"?_=null:r.target&&typeof r.target.value=="string"?(C=r.target.value,_=ke(b,C)):!r.target&&r.kind==="VariableDefinition"?(C=r,_=C.variable):typeof r.kind=="string"&&(_=r),e.props.modifyArguments((F.arguments||[]).map(function(A){return A===d?k({},A,{value:_}):A}),o)},e._setArgFields=function(r,o){var p=e.props.selection,m=e._getArgSelection();if(!m){console.error("missing arg selection when setting arg value");return}return e.props.modifyArguments((p.arguments||[]).map(function(h){return h===m?k({},h,{value:{kind:"ObjectValue",fields:r}}):h}),o)},n),w(e,l)}return P(t,[{key:"render",value:function(){var n=this.props,e=n.arg,l=n.parentField,c=this._getArgSelection();return a.createElement(Ee,{argValue:c?c.value:null,arg:e,parentField:l,addArg:this._addArg,removeArg:this._removeArg,setArgFields:this._setArgFields,setArgValue:this._setArgValue,getDefaultScalarArgValue:this.props.getDefaultScalarArgValue,makeDefaultArg:this.props.makeDefaultArg,onRunOperation:this.props.onRunOperation,styleConfig:this.props.styleConfig,onCommit:this.props.onCommit,definition:this.props.definition})}}]),t}(a.PureComponent);function Qe(i){return i.ctrlKey&&i.key==="Enter"}function Ze(i){return i!=="FragmentDefinition"}var Ke=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u0?C=""+b+g:C=b;var _=c.type.toString(),A=(0,v.parseType)(_),D={kind:"VariableDefinition",variable:{kind:"Variable",name:{kind:"Name",value:C}},type:A,directives:[]},B=function(x){return(n.props.definition.variableDefinitions||[]).find(function(T){return T.variable.name.value===x})},j=void 0,W={};if(typeof l<"u"&&l!==null){var U=(0,v.visit)(l,{Variable:function(x){var T=x.name.value,J=B(T);if(W[T]=W[T]+1||1,!!J)return J.defaultValue}}),K=D.type.kind==="NonNullType",M=K?k({},D,{type:D.type.type}):D;j=k({},M,{defaultValue:U})}else j=D;var $=Object.entries(W).filter(function(E){var x=ye(E,2);x[0];var T=x[1];return T<2}).map(function(E){var x=ye(E,2),T=x[0];return x[1],T});if(j){var X=n.props.setArgValue(j,!1);if(X){var ee=X.definitions.find(function(E){return E.operation&&E.name&&E.name.value&&n.props.definition.name&&n.props.definition.name.value?E.name.value===n.props.definition.name.value:!1}),y=[].concat(I(ee.variableDefinitions||[]),[j]).filter(function(E){return $.indexOf(E.variable.name.value)===-1}),S=k({},ee,{variableDefinitions:y}),O=X.definitions,V=O.map(function(E){return ee===E?S:E}),L=k({},X,{definitions:V});n.props.onCommit(L)}}},m=function(){if(!(!l||!l.name||!l.name.value)){var b=l.name.value,g=(n.props.definition.variableDefinitions||[]).find(function(M){return M.variable.name.value===b});if(g){var C=g.defaultValue,_=n.props.setArgValue(C,{commit:!1});if(_){var A=_.definitions.find(function(M){return M.name.value===n.props.definition.name.value});if(!A)return;var D=0;(0,v.visit)(A,{Variable:function($){$.name.value===b&&(D=D+1)}});var B=A.variableDefinitions||[];D<2&&(B=B.filter(function(M){return M.variable.name.value!==b}));var j=k({},A,{variableDefinitions:B}),W=_.definitions,U=W.map(function(M){return A===M?j:M}),K=k({},_,{definitions:U});n.props.onCommit(K)}}}},h=l&&l.kind==="Variable",F=this.state.displayArgActions?a.createElement("button",{type:"submit",className:"toolbar-button",title:h?"Remove the variable":"Extract the current value into a GraphQL variable",onClick:function(b){b.preventDefault(),b.stopPropagation(),h?m():p()},style:f.styles.actionButtonStyle},a.createElement("span",{style:{color:f.colors.variable}},"$")):null;return a.createElement("div",{style:{cursor:"pointer",minHeight:"16px",WebkitUserSelect:"none",userSelect:"none"},"data-arg-name":c.name,"data-arg-type":u.name,className:"graphiql-explorer-"+c.name},a.createElement("span",{style:{cursor:"pointer"},onClick:function(b){var g=!l;g?n.props.addArg(!0):n.props.removeArg(!0),n.setState({displayArgActions:g})}},(0,v.isInputObjectType)(u)?a.createElement("span",null,l?this.props.styleConfig.arrowOpen:this.props.styleConfig.arrowClosed):a.createElement(ae,{checked:!!l,styleConfig:this.props.styleConfig}),a.createElement("span",{style:{color:f.colors.attribute},title:c.description,onMouseEnter:function(){l!==null&&typeof l<"u"&&n.setState({displayArgActions:!0})},onMouseLeave:function(){return n.setState({displayArgActions:!1})}},c.name,Se(c)?"*":"",": ",F," ")," "),r||a.createElement("span",null)," ")}}]),t}(a.PureComponent),Je=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u0;C&&n.setState({displayFieldActions:!0})},onMouseLeave:function(){return n.setState({displayFieldActions:!1})}},(0,v.isObjectType)(o)?a.createElement("span",null,r?this.props.styleConfig.arrowOpen:this.props.styleConfig.arrowClosed):null,(0,v.isObjectType)(o)?null:a.createElement(ae,{checked:!!r,styleConfig:this.props.styleConfig}),a.createElement("span",{style:{color:u.colors.property},className:"graphiql-explorer-field-view"},l.name),this.state.displayFieldActions?a.createElement("button",{type:"submit",className:"toolbar-button",title:"Extract selections into a new reusable fragment",onClick:function(C){C.preventDefault(),C.stopPropagation();var _=o.name,A=_+"Fragment",D=(h||[]).filter(function(M){return M.name.value.startsWith(A)}).length;D>0&&(A=""+A+D);var B=r?r.selectionSet?r.selectionSet.selections:[]:[],j=[{kind:"FragmentSpread",name:{kind:"Name",value:A},directives:[]}],W={kind:"FragmentDefinition",name:{kind:"Name",value:A},typeCondition:{kind:"NamedType",name:{kind:"Name",value:o.name}},directives:[],selectionSet:{kind:"SelectionSet",selections:B}},U=n._modifyChildSelections(j,!1);if(U){var K=k({},U,{definitions:[].concat(I(U.definitions),[W])});n.props.onCommit(K)}else console.warn("Unable to complete extractFragment operation")},style:k({},u.styles.actionButtonStyle)},a.createElement("span",null,"…")):null),r&&p.length?a.createElement("div",{style:{marginLeft:16},className:"graphiql-explorer-graphql-arguments"},p.map(function(g){return a.createElement(We,{key:g.name,parentField:l,arg:g,selection:r,modifyArguments:n._setArguments,getDefaultScalarArgValue:n.props.getDefaultScalarArgValue,makeDefaultArg:n.props.makeDefaultArg,onRunOperation:n.props.onRunOperation,styleConfig:n.props.styleConfig,onCommit:n.props.onCommit,definition:n.props.definition})})):null);if(r&&((0,v.isObjectType)(o)||(0,v.isInterfaceType)(o)||(0,v.isUnionType)(o))){var d=(0,v.isUnionType)(o)?{}:o.getFields(),b=r?r.selectionSet?r.selectionSet.selections:[]:[];return a.createElement("div",{className:"graphiql-explorer-"+l.name},F,a.createElement("div",{style:{marginLeft:16}},h?h.map(function(g){var C=c.getType(g.typeCondition.name.value),_=g.name.value;return C?a.createElement(Ye,{key:_,fragment:g,selections:b,modifySelections:n._modifyChildSelections,schema:c,styleConfig:n.props.styleConfig,onCommit:n.props.onCommit}):null}):null,Object.keys(d).sort().map(function(g){return a.createElement(t,{key:g,field:d[g],selections:b,modifySelections:n._modifyChildSelections,schema:c,getDefaultFieldNames:f,getDefaultScalarArgValue:n.props.getDefaultScalarArgValue,makeDefaultArg:n.props.makeDefaultArg,onRunOperation:n.props.onRunOperation,styleConfig:n.props.styleConfig,onCommit:n.props.onCommit,definition:n.props.definition,availableFragments:n.props.availableFragments})}),(0,v.isInterfaceType)(o)||(0,v.isUnionType)(o)?c.getPossibleTypes(o).map(function(g){return a.createElement(Je,{key:g.name,implementingType:g,selections:b,modifySelections:n._modifyChildSelections,schema:c,getDefaultFieldNames:f,getDefaultScalarArgValue:n.props.getDefaultScalarArgValue,makeDefaultArg:n.props.makeDefaultArg,onRunOperation:n.props.onRunOperation,styleConfig:n.props.styleConfig,onCommit:n.props.onCommit,definition:n.props.definition})}):null))}return F}}]),t}(a.PureComponent);function Xe(i){try{return i.trim()?(0,v.parse)(i,{noLocation:!0}):null}catch(t){return new Error(t)}}var $e={kind:"OperationDefinition",operation:"query",variableDefinitions:[],name:{kind:"Name",value:"MyQuery"},directives:[],selectionSet:{kind:"SelectionSet",selections:[]}},le={kind:"Document",definitions:[$e]},Y=null;function et(i){if(Y&&Y[0]===i)return Y[1];var t=Xe(i);return t?t instanceof Error?Y?Y[1]:le:(Y=[i,t],t):le}var Oe={buttonStyle:{fontSize:"1.2em",padding:"0px",backgroundColor:"white",border:"none",margin:"5px 0px",height:"40px",width:"100%",display:"block",maxWidth:"none"},actionButtonStyle:{padding:"0px",backgroundColor:"white",border:"none",margin:"0px",maxWidth:"none",height:"15px",width:"15px",display:"inline-block",fontSize:"smaller"},explorerActionsStyle:{margin:"4px -8px -8px",paddingLeft:"8px",bottom:"0px",width:"100%",textAlign:"center",background:"none",borderTop:"none",borderBottom:"none"}},tt=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u"u"?"undefined":je(Z))==="object"&&typeof Z.commit<"u"?me=Z.commit:me=!0,H){var de=k({},d,{definitions:d.definitions.map(function(Ve){return Ve===y?H:Ve})});return me&&te(de),de}else return d},schema:l,getDefaultFieldNames:b,getDefaultScalarArgValue:g,makeDefaultArg:f,onRunOperation:function(){n.props.onRunOperation&&n.props.onRunOperation(O)},styleConfig:u,availableFragments:X})}),ee),K)}}]),t}(a.PureComponent);we.defaultProps={getDefaultFieldNames:be,getDefaultScalarArgValue:Ce};var rt=function(i){R(t,i);function t(){var s,n,e,l;q(this,t);for(var c=arguments.length,f=Array(c),u=0;u{f&&t(f),n()},[n,t]),[l,c]=G.useOptimisticState(G.useOperationsEditorState());return N.createElement(Fe,{schema:s,onRunOperation:e,explorerIsOpen:!0,colors:ot,arrowOpen:lt,arrowClosed:st,checkboxUnchecked:ut,checkboxChecked:pt,styles:ct,query:l,onEdit:c,...i})}function mt(i){return{title:"GraphiQL Explorer",icon:()=>N.createElement("svg",{height:"1em",strokeWidth:"1.5",viewBox:"0 0 24 24",fill:"none"},N.createElement("path",{d:"M18 6H20M22 6H20M20 6V4M20 6V8",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round"}),N.createElement("path",{d:"M21.4 20H2.6C2.26863 20 2 19.7314 2 19.4V11H21.4C21.7314 11 22 11.2686 22 11.6V19.4C22 19.7314 21.7314 20 21.4 20Z",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round"}),N.createElement("path",{d:"M2 11V4.6C2 4.26863 2.26863 4 2.6 4H8.77805C8.92127 4 9.05977 4.05124 9.16852 4.14445L12.3315 6.85555C12.4402 6.94876 12.5787 7 12.722 7H14",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round"})),content:()=>N.createElement(ft,{...i})}}z.explorerPlugin=mt,Object.defineProperty(z,Symbol.toStringTag,{value:"Module"})}); diff --git a/netbox/project-static/dist/graphiql/react-dom.production.min.js b/netbox/project-static/dist/graphiql/react-dom.production.min.js index e8a0213d7..fb4e099c0 100644 --- a/netbox/project-static/dist/graphiql/react-dom.production.min.js +++ b/netbox/project-static/dist/graphiql/react-dom.production.min.js @@ -10,258 +10,258 @@ (function(){/* Modernizr 3.0.0pre (Custom Build) | MIT */ -'use strict';(function(Q,mb){"object"===typeof exports&&"undefined"!==typeof module?mb(exports,require("react")):"function"===typeof define&&define.amd?define(["exports","react"],mb):(Q=Q||self,mb(Q.ReactDOM={},Q.React))})(this,function(Q,mb){function n(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;cb}return!1}function Y(a,b,c,d,e,f,g){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f;this.removeEmptyString=g}function $d(a,b,c,d){var e=R.hasOwnProperty(b)?R[b]:null;if(null!==e?0!==e.type:d||!(2h||e[g]!==f[h]){var k="\n"+e[g].replace(" at new "," at ");a.displayName&&k.includes("")&&(k=k.replace("",a.displayName));return k}while(1<=g&&0<=h)}break}}}finally{ce=!1,Error.prepareStackTrace=c}return(a=a?a.displayName||a.name:"")?bc(a): -""}function gj(a){switch(a.tag){case 5:return bc(a.type);case 16:return bc("Lazy");case 13:return bc("Suspense");case 19:return bc("SuspenseList");case 0:case 2:case 15:return a=be(a.type,!1),a;case 11:return a=be(a.type.render,!1),a;case 1:return a=be(a.type,!0),a;default:return""}}function de(a){if(null==a)return null;if("function"===typeof a)return a.displayName||a.name||null;if("string"===typeof a)return a;switch(a){case Bb:return"Fragment";case Cb:return"Portal";case ee:return"Profiler";case fe:return"StrictMode"; -case ge:return"Suspense";case he:return"SuspenseList"}if("object"===typeof a)switch(a.$$typeof){case gg:return(a.displayName||"Context")+".Consumer";case hg:return(a._context.displayName||"Context")+".Provider";case ie:var b=a.render;a=a.displayName;a||(a=b.displayName||b.name||"",a=""!==a?"ForwardRef("+a+")":"ForwardRef");return a;case je:return b=a.displayName||null,null!==b?b:de(a.type)||"Memo";case Ta:b=a._payload;a=a._init;try{return de(a(b))}catch(c){}}return null}function hj(a){var b=a.type; +'use strict';(function(Q,zb){"object"===typeof exports&&"undefined"!==typeof module?zb(exports,require("react")):"function"===typeof define&&define.amd?define(["exports","react"],zb):(Q=Q||self,zb(Q.ReactDOM={},Q.React))})(this,function(Q,zb){function m(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;cb}return!1}function Y(a,b,c,d,e,f,g){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f;this.removeEmptyString=g}function $d(a,b,c,d){var e=R.hasOwnProperty(b)?R[b]:null;if(null!==e?0!==e.type:d||!(2h||e[g]!==f[h]){var k="\n"+e[g].replace(" at new "," at ");a.displayName&&k.includes("")&&(k=k.replace("",a.displayName));return k}while(1<=g&&0<=h)}break}}}finally{ce=!1,Error.prepareStackTrace=c}return(a=a?a.displayName||a.name:"")?bc(a): +""}function fj(a){switch(a.tag){case 5:return bc(a.type);case 16:return bc("Lazy");case 13:return bc("Suspense");case 19:return bc("SuspenseList");case 0:case 2:case 15:return a=be(a.type,!1),a;case 11:return a=be(a.type.render,!1),a;case 1:return a=be(a.type,!0),a;default:return""}}function de(a){if(null==a)return null;if("function"===typeof a)return a.displayName||a.name||null;if("string"===typeof a)return a;switch(a){case Bb:return"Fragment";case Cb:return"Portal";case ee:return"Profiler";case fe:return"StrictMode"; +case ge:return"Suspense";case he:return"SuspenseList"}if("object"===typeof a)switch(a.$$typeof){case gg:return(a.displayName||"Context")+".Consumer";case hg:return(a._context.displayName||"Context")+".Provider";case ie:var b=a.render;a=a.displayName;a||(a=b.displayName||b.name||"",a=""!==a?"ForwardRef("+a+")":"ForwardRef");return a;case je:return b=a.displayName||null,null!==b?b:de(a.type)||"Memo";case Ta:b=a._payload;a=a._init;try{return de(a(b))}catch(c){}}return null}function gj(a){var b=a.type; switch(a.tag){case 24:return"Cache";case 9:return(b.displayName||"Context")+".Consumer";case 10:return(b._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return a=b.render,a=a.displayName||a.name||"",b.displayName||(""!==a?"ForwardRef("+a+")":"ForwardRef");case 7:return"Fragment";case 5:return b;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return de(b);case 8:return b===fe?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler"; case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if("function"===typeof b)return b.displayName||b.name||null;if("string"===typeof b)return b}return null}function Ua(a){switch(typeof a){case "boolean":case "number":case "string":case "undefined":return a;case "object":return a;default:return""}}function ig(a){var b=a.type;return(a=a.nodeName)&&"input"===a.toLowerCase()&&("checkbox"===b||"radio"=== -b)}function ij(a){var b=ig(a)?"checked":"value",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=""+a[b];if(!a.hasOwnProperty(b)&&"undefined"!==typeof c&&"function"===typeof c.get&&"function"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=""+a;f.call(this,a)}});Object.defineProperty(a,b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=""+a},stopTracking:function(){a._valueTracker= -null;delete a[b]}}}}function Pc(a){a._valueTracker||(a._valueTracker=ij(a))}function jg(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d="";a&&(d=ig(a)?a.checked?"true":"false":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Qc(a){a=a||("undefined"!==typeof document?document:void 0);if("undefined"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}function ke(a,b){var c=b.checked;return E({},b,{defaultChecked:void 0,defaultValue:void 0, +b)}function hj(a){var b=ig(a)?"checked":"value",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=""+a[b];if(!a.hasOwnProperty(b)&&"undefined"!==typeof c&&"function"===typeof c.get&&"function"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=""+a;f.call(this,a)}});Object.defineProperty(a,b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=""+a},stopTracking:function(){a._valueTracker= +null;delete a[b]}}}}function Pc(a){a._valueTracker||(a._valueTracker=hj(a))}function jg(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d="";a&&(d=ig(a)?a.checked?"true":"false":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Qc(a){a=a||("undefined"!==typeof document?document:void 0);if("undefined"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}function ke(a,b){var c=b.checked;return E({},b,{defaultChecked:void 0,defaultValue:void 0, value:void 0,checked:null!=c?c:a._wrapperState.initialChecked})}function kg(a,b){var c=null==b.defaultValue?"":b.defaultValue,d=null!=b.checked?b.checked:b.defaultChecked;c=Ua(null!=b.value?b.value:c);a._wrapperState={initialChecked:d,initialValue:c,controlled:"checkbox"===b.type||"radio"===b.type?null!=b.checked:null!=b.value}}function lg(a,b){b=b.checked;null!=b&&$d(a,"checked",b,!1)}function le(a,b){lg(a,b);var c=Ua(b.value),d=b.type;if(null!=c)if("number"===d){if(0===c&&""===a.value||a.value!= c)a.value=""+c}else a.value!==""+c&&(a.value=""+c);else if("submit"===d||"reset"===d){a.removeAttribute("value");return}b.hasOwnProperty("value")?me(a,b.type,c):b.hasOwnProperty("defaultValue")&&me(a,b.type,Ua(b.defaultValue));null==b.checked&&null!=b.defaultChecked&&(a.defaultChecked=!!b.defaultChecked)}function mg(a,b,c){if(b.hasOwnProperty("value")||b.hasOwnProperty("defaultValue")){var d=b.type;if(!("submit"!==d&&"reset"!==d||void 0!==b.value&&null!==b.value))return;b=""+a._wrapperState.initialValue; c||b===a.value||(a.value=b);a.defaultValue=b}c=a.name;""!==c&&(a.name="");a.defaultChecked=!!a._wrapperState.initialChecked;""!==c&&(a.name=c)}function me(a,b,c){if("number"!==b||Qc(a.ownerDocument)!==a)null==c?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+c&&(a.defaultValue=""+c)}function Db(a,b,c,d){a=a.options;if(b){b={};for(var e=0;e>>=0;return 0===a?32:31-(rj(a)/sj|0)|0}function hc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a& +if(c&&"function"!==typeof c)throw Error(m(231,b,typeof c));return c}function jj(a,b,c,d,e,f,g,h,k){gc=!1;Sc=null;kj.apply(lj,arguments)}function mj(a,b,c,d,e,f,g,h,k){jj.apply(this,arguments);if(gc){if(gc){var n=Sc;gc=!1;Sc=null}else throw Error(m(198));Tc||(Tc=!0,ue=n)}}function nb(a){var b=a,c=a;if(a.alternate)for(;b.return;)b=b.return;else{a=b;do b=a,0!==(b.flags&4098)&&(c=b.return),a=b.return;while(a)}return 3===b.tag?c:null}function zg(a){if(13===a.tag){var b=a.memoizedState;null===b&&(a=a.alternate, +null!==a&&(b=a.memoizedState));if(null!==b)return b.dehydrated}return null}function Ag(a){if(nb(a)!==a)throw Error(m(188));}function nj(a){var b=a.alternate;if(!b){b=nb(a);if(null===b)throw Error(m(188));return b!==a?null:a}for(var c=a,d=b;;){var e=c.return;if(null===e)break;var f=e.alternate;if(null===f){d=e.return;if(null!==d){c=d;continue}break}if(e.child===f.child){for(f=e.child;f;){if(f===c)return Ag(e),a;if(f===d)return Ag(e),b;f=f.sibling}throw Error(m(188));}if(c.return!==d.return)c=e,d=f; +else{for(var g=!1,h=e.child;h;){if(h===c){g=!0;c=e;d=f;break}if(h===d){g=!0;d=e;c=f;break}h=h.sibling}if(!g){for(h=f.child;h;){if(h===c){g=!0;c=f;d=e;break}if(h===d){g=!0;d=f;c=e;break}h=h.sibling}if(!g)throw Error(m(189));}}if(c.alternate!==d)throw Error(m(190));}if(3!==c.tag)throw Error(m(188));return c.stateNode.current===c?a:b}function Bg(a){a=nj(a);return null!==a?Cg(a):null}function Cg(a){if(5===a.tag||6===a.tag)return a;for(a=a.child;null!==a;){var b=Cg(a);if(null!==b)return b;a=a.sibling}return null} +function oj(a,b){if(Ca&&"function"===typeof Ca.onCommitFiberRoot)try{Ca.onCommitFiberRoot(Uc,a,void 0,128===(a.current.flags&128))}catch(c){}}function pj(a){a>>>=0;return 0===a?32:31-(qj(a)/rj|0)|0}function hc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a& 4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return a}}function Vc(a,b){var c=a.pendingLanes;if(0===c)return 0;var d=0,e=a.suspendedLanes,f=a.pingedLanes,g=c&268435455;if(0!==g){var h=g&~e;0!==h?d=hc(h):(f&=g,0!==f&&(d=hc(f)))}else g=c&~e,0!==g?d=hc(g):0!==f&&(d=hc(f));if(0===d)return 0;if(0!==b&&b!==d&&0===(b&e)&& -(e=d&-d,f=b&-b,e>=f||16===e&&0!==(f&4194240)))return b;0!==(d&4)&&(d|=c&16);b=a.entangledLanes;if(0!==b)for(a=a.entanglements,b&=d;0c;c++)b.push(a); -return b}function ic(a,b,c){a.pendingLanes|=b;536870912!==b&&(a.suspendedLanes=0,a.pingedLanes=0);a=a.eventTimes;b=31-ta(b);a[b]=c}function vj(a,b){var c=a.pendingLanes&~b;a.pendingLanes=b;a.suspendedLanes=0;a.pingedLanes=0;a.expiredLanes&=b;a.mutableReadLanes&=b;a.entangledLanes&=b;b=a.entanglements;var d=a.eventTimes;for(a=a.expirationTimes;0=f||16===e&&0!==(f&4194240)))return b;0!==(d&4)&&(d|=c&16);b=a.entangledLanes;if(0!==b)for(a=a.entanglements,b&=d;0c;c++)b.push(a); +return b}function ic(a,b,c){a.pendingLanes|=b;536870912!==b&&(a.suspendedLanes=0,a.pingedLanes=0);a=a.eventTimes;b=31-ta(b);a[b]=c}function uj(a,b){var c=a.pendingLanes&~b;a.pendingLanes=b;a.suspendedLanes=0;a.pingedLanes=0;a.expiredLanes&=b;a.mutableReadLanes&=b;a.entangledLanes&=b;b=a.entanglements;var d=a.eventTimes;for(a=a.expirationTimes;0=b)return{node:c,offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=$g(c)}}function bh(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?bh(a,b.parentNode):"contains"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}function ch(){for(var a=window,b=Qc();b instanceof a.HTMLIFrameElement;){try{var c="string"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break; -b=Qc(a.document)}return b}function Ie(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&("input"===b&&("text"===a.type||"search"===a.type||"tel"===a.type||"url"===a.type||"password"===a.type)||"textarea"===b||"true"===a.contentEditable)}function Uj(a){var b=ch(),c=a.focusedElem,d=a.selectionRange;if(b!==c&&c&&c.ownerDocument&&bh(c.ownerDocument.documentElement,c)){if(null!==d&&Ie(c))if(b=d.start,a=d.end,void 0===a&&(a=b),"selectionStart"in c)c.selectionStart=b,c.selectionEnd=Math.min(a,c.value.length); +b=Qc(a.document)}return b}function Ie(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&("input"===b&&("text"===a.type||"search"===a.type||"tel"===a.type||"url"===a.type||"password"===a.type)||"textarea"===b||"true"===a.contentEditable)}function Tj(a){var b=ch(),c=a.focusedElem,d=a.selectionRange;if(b!==c&&c&&c.ownerDocument&&bh(c.ownerDocument.documentElement,c)){if(null!==d&&Ie(c))if(b=d.start,a=d.end,void 0===a&&(a=b),"selectionStart"in c)c.selectionStart=b,c.selectionEnd=Math.min(a,c.value.length); else if(a=(b=c.ownerDocument||document)&&b.defaultView||window,a.getSelection){a=a.getSelection();var e=c.textContent.length,f=Math.min(d.start,e);d=void 0===d.end?f:Math.min(d.end,e);!a.extend&&f>d&&(e=d,d=f,f=e);e=ah(c,f);var g=ah(c,d);e&&g&&(1!==a.rangeCount||a.anchorNode!==e.node||a.anchorOffset!==e.offset||a.focusNode!==g.node||a.focusOffset!==g.offset)&&(b=b.createRange(),b.setStart(e.node,e.offset),a.removeAllRanges(),f>d?(a.addRange(b),a.extend(g.node,g.offset)):(b.setEnd(g.node,g.offset), a.addRange(b)))}b=[];for(a=c;a=a.parentNode;)1===a.nodeType&&b.push({element:a,left:a.scrollLeft,top:a.scrollTop});"function"===typeof c.focus&&c.focus();for(c=0;cMb||(a.current=Se[Mb],Se[Mb]=null,Mb--)} -function y(a,b,c){Mb++;Se[Mb]=a.current;a.current=b}function Nb(a,b){var c=a.type.contextTypes;if(!c)return cb;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function ea(a){a=a.childContextTypes;return null!==a&&void 0!==a}function th(a,b,c){if(J.current!==cb)throw Error(n(168)); -y(J,b);y(S,c)}function uh(a,b,c){var d=a.stateNode;b=b.childContextTypes;if("function"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in b))throw Error(n(108,hj(a)||"Unknown",e));return E({},c,d)}function ld(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||cb;qb=J.current;y(J,a);y(S,S.current);return!0}function vh(a,b,c){var d=a.stateNode;if(!d)throw Error(n(169));c?(a=uh(a,b,qb),d.__reactInternalMemoizedMergedChildContext=a,w(S),w(J),y(J,a)):w(S); -y(S,c)}function wh(a){null===La?La=[a]:La.push(a)}function kk(a){md=!0;wh(a)}function db(){if(!Te&&null!==La){Te=!0;var a=0,b=z;try{var c=La;for(z=1;a>=g;e-=g;Ma=1<<32-ta(b)+e|c<q?(v=l,l=null):v=l.sibling;var A=r(e,l,h[q],k);if(null===A){null===l&&(l=v);break}a&&l&&null===A.alternate&&b(e,l);g=f(A,g,q);null===m?n=A:m.sibling=A;m=A;l=v}if(q===h.length)return c(e,l),D&&rb(e,q),n;if(null===l){for(;qv?(A=q,q=null):A=q.sibling;var x=r(e,q,t.value,k);if(null===x){null===q&&(q=A);break}a&&q&&null===x.alternate&&b(e,q);g=f(x,g,v);null===l?m=x:l.sibling=x;l=x;q=A}if(t.done)return c(e,q),D&&rb(e,v),m; -if(null===q){for(;!t.done;v++,t=h.next())t=u(e,t.value,k),null!==t&&(g=f(t,g,v),null===l?m=t:l.sibling=t,l=t);D&&rb(e,v);return m}for(q=d(e,q);!t.done;v++,t=h.next())t=p(q,e,v,t.value,k),null!==t&&(a&&null!==t.alternate&&q.delete(null===t.key?v:t.key),g=f(t,g,v),null===l?m=t:l.sibling=t,l=t);a&&q.forEach(function(a){return b(e,a)});D&&rb(e,v);return m}function w(a,d,f,h){"object"===typeof f&&null!==f&&f.type===Bb&&null===f.key&&(f=f.props.children);if("object"===typeof f&&null!==f){switch(f.$$typeof){case xd:a:{for(var k= -f.key,m=d;null!==m;){if(m.key===k){k=f.type;if(k===Bb){if(7===m.tag){c(a,m.sibling);d=e(m,f.props.children);d.return=a;a=d;break a}}else if(m.elementType===k||"object"===typeof k&&null!==k&&k.$$typeof===Ta&&Kh(k)===m.type){c(a,m.sibling);d=e(m,f.props);d.ref=vc(a,m,f);d.return=a;a=d;break a}c(a,m);break}else b(a,m);m=m.sibling}f.type===Bb?(d=ub(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=wd(f.type,f.key,f.props,null,a.mode,h),h.ref=vc(a,d,f),h.return=a,a=h)}return g(a);case Cb:a:{for(m=f.key;null!== -d;){if(d.key===m)if(4===d.tag&&d.stateNode.containerInfo===f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=hf(f,a.mode,h);d.return=a;a=d}return g(a);case Ta:return m=f._init,w(a,d,m(f._payload),h)}if(cc(f))return x(a,d,f,h);if(ac(f))return F(a,d,f,h);vd(a,f)}return"string"===typeof f&&""!==f||"number"===typeof f?(f=""+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d): -(c(a,d),d=gf(f,a.mode,h),d.return=a,a=d),g(a)):c(a,d)}return w}function vb(a){if(a===wc)throw Error(n(174));return a}function jf(a,b){y(xc,b);y(yc,a);y(Ea,wc);a=b.nodeType;switch(a){case 9:case 11:b=(b=b.documentElement)?b.namespaceURI:oe(null,"");break;default:a=8===a?b.parentNode:b,b=a.namespaceURI||null,a=a.tagName,b=oe(b,a)}w(Ea);y(Ea,b)}function Tb(a){w(Ea);w(yc);w(xc)}function Mh(a){vb(xc.current);var b=vb(Ea.current);var c=oe(b,a.type);b!==c&&(y(yc,a),y(Ea,c))}function kf(a){yc.current===a&& -(w(Ea),w(yc))}function yd(a){for(var b=a;null!==b;){if(13===b.tag){var c=b.memoizedState;if(null!==c&&(c=c.dehydrated,null===c||"$?"===c.data||"$!"===c.data))return b}else if(19===b.tag&&void 0!==b.memoizedProps.revealOrder){if(0!==(b.flags&128))return b}else if(null!==b.child){b.child.return=b;b=b.child;continue}if(b===a)break;for(;null===b.sibling;){if(null===b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}return null}function lf(){for(var a=0;ac?c:4;a(!0);var d=uf.transition;uf.transition={};try{a(!1),b()}finally{z=c,uf.transition=d}}function di(){return sa().memoizedState}function rk(a,b, -c){var d=hb(a);c={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ei(a))fi(b,c);else if(c=Ch(a,b,c,d),null!==c){var e=Z();ya(c,a,d,e);gi(c,b,d)}}function pk(a,b,c){var d=hb(a),e={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ei(a))fi(b,e);else{var f=a.alternate;if(0===a.lanes&&(null===f||0===f.lanes)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState,h=f(g,c);e.hasEagerState=!0;e.eagerState=h;if(ua(h,g)){var k=b.interleaved;null===k?(e.next=e,cf(b)): -(e.next=k.next,k.next=e);b.interleaved=e;return}}catch(m){}finally{}c=Ch(a,b,e,d);null!==c&&(e=Z(),ya(c,a,d,e),gi(c,b,d))}}function ei(a){var b=a.alternate;return a===C||null!==b&&b===C}function fi(a,b){zc=Bd=!0;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}function gi(a,b,c){if(0!==(c&4194240)){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function Ub(a,b){try{var c="",d=b;do c+=gj(d),d=d.return;while(d);var e=c}catch(f){e="\nError generating stack: "+f.message+ -"\n"+f.stack}return{value:a,source:b,stack:e,digest:null}}function vf(a,b,c){return{value:a,source:null,stack:null!=c?c:null,digest:null!=b?b:null}}function wf(a,b){try{console.error(b.value)}catch(c){setTimeout(function(){throw c;})}}function hi(a,b,c){c=Pa(-1,c);c.tag=3;c.payload={element:null};var d=b.value;c.callback=function(){Ed||(Ed=!0,xf=d);wf(a,b)};return c}function ii(a,b,c){c=Pa(-1,c);c.tag=3;var d=a.type.getDerivedStateFromError;if("function"===typeof d){var e=b.value;c.payload=function(){return d(e)}; -c.callback=function(){wf(a,b)}}var f=a.stateNode;null!==f&&"function"===typeof f.componentDidCatch&&(c.callback=function(){wf(a,b);"function"!==typeof d&&(null===ib?ib=new Set([this]):ib.add(this));var c=b.stack;this.componentDidCatch(b.value,{componentStack:null!==c?c:""})});return c}function ji(a,b,c){var d=a.pingCache;if(null===d){d=a.pingCache=new sk;var e=new Set;d.set(b,e)}else e=d.get(b),void 0===e&&(e=new Set,d.set(b,e));e.has(c)||(e.add(c),a=tk.bind(null,a,b,c),b.then(a,a))}function ki(a){do{var b; -if(b=13===a.tag)b=a.memoizedState,b=null!==b?null!==b.dehydrated?!0:!1:!0;if(b)return a;a=a.return}while(null!==a);return null}function li(a,b,c,d,e){if(0===(a.mode&1))return a===b?a.flags|=65536:(a.flags|=128,c.flags|=131072,c.flags&=-52805,1===c.tag&&(null===c.alternate?c.tag=17:(b=Pa(-1,1),b.tag=2,eb(c,b,1))),c.lanes|=1),a;a.flags|=65536;a.lanes=e;return a}function aa(a,b,c,d){b.child=null===a?mi(b,null,c,d):Vb(b,a.child,c,d)}function ni(a,b,c,d,e){c=c.render;var f=b.ref;Sb(b,e);d=of(a,b,c,d,f, -e);c=pf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&c&&Ue(b);b.flags|=1;aa(a,b,d,e);return b.child}function oi(a,b,c,d,e){if(null===a){var f=c.type;if("function"===typeof f&&!yf(f)&&void 0===f.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=f,pi(a,b,f,d,e);a=wd(c.type,null,d,b,b.mode,e);a.ref=b.ref;a.return=b;return b.child=a}f=a.child;if(0===(a.lanes&e)){var g=f.memoizedProps;c=c.compare;c=null!==c?c:qc;if(c(g,d)&&a.ref=== -b.ref)return Qa(a,b,e)}b.flags|=1;a=gb(f,d);a.ref=b.ref;a.return=b;return b.child=a}function pi(a,b,c,d,e){if(null!==a){var f=a.memoizedProps;if(qc(f,d)&&a.ref===b.ref)if(ha=!1,b.pendingProps=d=f,0!==(a.lanes&e))0!==(a.flags&131072)&&(ha=!0);else return b.lanes=a.lanes,Qa(a,b,e)}return zf(a,b,c,d,e)}function qi(a,b,c){var d=b.pendingProps,e=d.children,f=null!==a?a.memoizedState:null;if("hidden"===d.mode)if(0===(b.mode&1))b.memoizedState={baseLanes:0,cachePool:null,transitions:null},y(Ga,ba),ba|=c; -else{if(0===(c&1073741824))return a=null!==f?f.baseLanes|c:c,b.lanes=b.childLanes=1073741824,b.memoizedState={baseLanes:a,cachePool:null,transitions:null},b.updateQueue=null,y(Ga,ba),ba|=a,null;b.memoizedState={baseLanes:0,cachePool:null,transitions:null};d=null!==f?f.baseLanes:c;y(Ga,ba);ba|=d}else null!==f?(d=f.baseLanes|c,b.memoizedState=null):d=c,y(Ga,ba),ba|=d;aa(a,b,e,c);return b.child}function ri(a,b){var c=b.ref;if(null===a&&null!==c||null!==a&&a.ref!==c)b.flags|=512,b.flags|=2097152}function zf(a, -b,c,d,e){var f=ea(c)?qb:J.current;f=Nb(b,f);Sb(b,e);c=of(a,b,c,d,f,e);d=pf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&d&&Ue(b);b.flags|=1;aa(a,b,c,e);return b.child}function si(a,b,c,d,e){if(ea(c)){var f=!0;ld(b)}else f=!1;Sb(b,e);if(null===b.stateNode)Fd(a,b),Hh(b,c,d),ff(b,c,d,e),d=!0;else if(null===a){var g=b.stateNode,h=b.memoizedProps;g.props=h;var k=g.context,m=c.contextType;"object"===typeof m&&null!==m?m=qa(m):(m=ea(c)?qb:J.current,m=Nb(b, -m));var l=c.getDerivedStateFromProps,n="function"===typeof l||"function"===typeof g.getSnapshotBeforeUpdate;n||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||k!==m)&&Ih(b,g,d,m);fb=!1;var r=b.memoizedState;g.state=r;td(b,d,g,e);k=b.memoizedState;h!==d||r!==k||S.current||fb?("function"===typeof l&&(ef(b,c,l,d),k=b.memoizedState),(h=fb||Gh(b,c,h,d,r,k,m))?(n||"function"!==typeof g.UNSAFE_componentWillMount&&"function"!==typeof g.componentWillMount|| -("function"===typeof g.componentWillMount&&g.componentWillMount(),"function"===typeof g.UNSAFE_componentWillMount&&g.UNSAFE_componentWillMount()),"function"===typeof g.componentDidMount&&(b.flags|=4194308)):("function"===typeof g.componentDidMount&&(b.flags|=4194308),b.memoizedProps=d,b.memoizedState=k),g.props=d,g.state=k,g.context=m,d=h):("function"===typeof g.componentDidMount&&(b.flags|=4194308),d=!1)}else{g=b.stateNode;Dh(a,b);h=b.memoizedProps;m=b.type===b.elementType?h:xa(b.type,h);g.props= -m;n=b.pendingProps;r=g.context;k=c.contextType;"object"===typeof k&&null!==k?k=qa(k):(k=ea(c)?qb:J.current,k=Nb(b,k));var p=c.getDerivedStateFromProps;(l="function"===typeof p||"function"===typeof g.getSnapshotBeforeUpdate)||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==n||r!==k)&&Ih(b,g,d,k);fb=!1;r=b.memoizedState;g.state=r;td(b,d,g,e);var x=b.memoizedState;h!==n||r!==x||S.current||fb?("function"===typeof p&&(ef(b,c,p,d),x=b.memoizedState), -(m=fb||Gh(b,c,m,d,r,x,k)||!1)?(l||"function"!==typeof g.UNSAFE_componentWillUpdate&&"function"!==typeof g.componentWillUpdate||("function"===typeof g.componentWillUpdate&&g.componentWillUpdate(d,x,k),"function"===typeof g.UNSAFE_componentWillUpdate&&g.UNSAFE_componentWillUpdate(d,x,k)),"function"===typeof g.componentDidUpdate&&(b.flags|=4),"function"===typeof g.getSnapshotBeforeUpdate&&(b.flags|=1024)):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|= -4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),b.memoizedProps=d,b.memoizedState=x),g.props=d,g.state=x,g.context=k,d=m):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),d=!1)}return Af(a,b,c,d,f,e)}function Af(a,b,c,d,e,f){ri(a,b);var g=0!==(b.flags&128);if(!d&&!g)return e&&vh(b,c,!1), -Qa(a,b,f);d=b.stateNode;uk.current=b;var h=g&&"function"!==typeof c.getDerivedStateFromError?null:d.render();b.flags|=1;null!==a&&g?(b.child=Vb(b,a.child,null,f),b.child=Vb(b,null,h,f)):aa(a,b,h,f);b.memoizedState=d.state;e&&vh(b,c,!0);return b.child}function ti(a){var b=a.stateNode;b.pendingContext?th(a,b.pendingContext,b.pendingContext!==b.context):b.context&&th(a,b.context,!1);jf(a,b.containerInfo)}function ui(a,b,c,d,e){Qb();Ye(e);b.flags|=256;aa(a,b,c,d);return b.child}function Bf(a){return{baseLanes:a, -cachePool:null,transitions:null}}function vi(a,b,c){var d=b.pendingProps,e=G.current,f=!1,g=0!==(b.flags&128),h;(h=g)||(h=null!==a&&null===a.memoizedState?!1:0!==(e&2));if(h)f=!0,b.flags&=-129;else if(null===a||null!==a.memoizedState)e|=1;y(G,e&1);if(null===a){Xe(b);a=b.memoizedState;if(null!==a&&(a=a.dehydrated,null!==a))return 0===(b.mode&1)?b.lanes=1:"$!"===a.data?b.lanes=8:b.lanes=1073741824,null;g=d.children;a=d.fallback;return f?(d=b.mode,f=b.child,g={mode:"hidden",children:g},0===(d&1)&&null!== -f?(f.childLanes=0,f.pendingProps=g):f=Gd(g,d,0,null),a=ub(a,d,c,null),f.return=b,a.return=b,f.sibling=a,b.child=f,b.child.memoizedState=Bf(c),b.memoizedState=Cf,a):Df(b,g)}e=a.memoizedState;if(null!==e&&(h=e.dehydrated,null!==h))return vk(a,b,g,d,h,e,c);if(f){f=d.fallback;g=b.mode;e=a.child;h=e.sibling;var k={mode:"hidden",children:d.children};0===(g&1)&&b.child!==e?(d=b.child,d.childLanes=0,d.pendingProps=k,b.deletions=null):(d=gb(e,k),d.subtreeFlags=e.subtreeFlags&14680064);null!==h?f=gb(h,f):(f= -ub(f,g,c,null),f.flags|=2);f.return=b;d.return=b;d.sibling=f;b.child=d;d=f;f=b.child;g=a.child.memoizedState;g=null===g?Bf(c):{baseLanes:g.baseLanes|c,cachePool:null,transitions:g.transitions};f.memoizedState=g;f.childLanes=a.childLanes&~c;b.memoizedState=Cf;return d}f=a.child;a=f.sibling;d=gb(f,{mode:"visible",children:d.children});0===(b.mode&1)&&(d.lanes=c);d.return=b;d.sibling=null;null!==a&&(c=b.deletions,null===c?(b.deletions=[a],b.flags|=16):c.push(a));b.child=d;b.memoizedState=null;return d} -function Df(a,b,c){b=Gd({mode:"visible",children:b},a.mode,0,null);b.return=a;return a.child=b}function Hd(a,b,c,d){null!==d&&Ye(d);Vb(b,a.child,null,c);a=Df(b,b.pendingProps.children);a.flags|=2;b.memoizedState=null;return a}function vk(a,b,c,d,e,f,g){if(c){if(b.flags&256)return b.flags&=-257,d=vf(Error(n(422))),Hd(a,b,g,d);if(null!==b.memoizedState)return b.child=a.child,b.flags|=128,null;f=d.fallback;e=b.mode;d=Gd({mode:"visible",children:d.children},e,0,null);f=ub(f,e,g,null);f.flags|=2;d.return= -b;f.return=b;d.sibling=f;b.child=d;0!==(b.mode&1)&&Vb(b,a.child,null,g);b.child.memoizedState=Bf(g);b.memoizedState=Cf;return f}if(0===(b.mode&1))return Hd(a,b,g,null);if("$!"===e.data){d=e.nextSibling&&e.nextSibling.dataset;if(d)var h=d.dgst;d=h;f=Error(n(419));d=vf(f,d,void 0);return Hd(a,b,g,d)}h=0!==(g&a.childLanes);if(ha||h){d=O;if(null!==d){switch(g&-g){case 4:e=2;break;case 16:e=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:e= -32;break;case 536870912:e=268435456;break;default:e=0}e=0!==(e&(d.suspendedLanes|g))?0:e;0!==e&&e!==f.retryLane&&(f.retryLane=e,Oa(a,e),ya(d,a,e,-1))}Ef();d=vf(Error(n(421)));return Hd(a,b,g,d)}if("$?"===e.data)return b.flags|=128,b.child=a.child,b=wk.bind(null,a),e._reactRetry=b,null;a=f.treeContext;fa=Ka(e.nextSibling);la=b;D=!0;wa=null;null!==a&&(na[oa++]=Ma,na[oa++]=Na,na[oa++]=sb,Ma=a.id,Na=a.overflow,sb=b);b=Df(b,d.children);b.flags|=4096;return b}function wi(a,b,c){a.lanes|=b;var d=a.alternate; -null!==d&&(d.lanes|=b);bf(a.return,b,c)}function Ff(a,b,c,d,e){var f=a.memoizedState;null===f?a.memoizedState={isBackwards:b,rendering:null,renderingStartTime:0,last:d,tail:c,tailMode:e}:(f.isBackwards=b,f.rendering=null,f.renderingStartTime=0,f.last=d,f.tail=c,f.tailMode=e)}function xi(a,b,c){var d=b.pendingProps,e=d.revealOrder,f=d.tail;aa(a,b,d.children,c);d=G.current;if(0!==(d&2))d=d&1|2,b.flags|=128;else{if(null!==a&&0!==(a.flags&128))a:for(a=b.child;null!==a;){if(13===a.tag)null!==a.memoizedState&& -wi(a,c,b);else if(19===a.tag)wi(a,c,b);else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===b)break a;for(;null===a.sibling;){if(null===a.return||a.return===b)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}d&=1}y(G,d);if(0===(b.mode&1))b.memoizedState=null;else switch(e){case "forwards":c=b.child;for(e=null;null!==c;)a=c.alternate,null!==a&&null===yd(a)&&(e=c),c=c.sibling;c=e;null===c?(e=b.child,b.child=null):(e=c.sibling,c.sibling=null);Ff(b,!1,e,c,f);break;case "backwards":c= -null;e=b.child;for(b.child=null;null!==e;){a=e.alternate;if(null!==a&&null===yd(a)){b.child=e;break}a=e.sibling;e.sibling=c;c=e;e=a}Ff(b,!0,c,null,f);break;case "together":Ff(b,!1,null,null,void 0);break;default:b.memoizedState=null}return b.child}function Fd(a,b){0===(b.mode&1)&&null!==a&&(a.alternate=null,b.alternate=null,b.flags|=2)}function Qa(a,b,c){null!==a&&(b.dependencies=a.dependencies);ra|=b.lanes;if(0===(c&b.childLanes))return null;if(null!==a&&b.child!==a.child)throw Error(n(153));if(null!== -b.child){a=b.child;c=gb(a,a.pendingProps);b.child=c;for(c.return=b;null!==a.sibling;)a=a.sibling,c=c.sibling=gb(a,a.pendingProps),c.return=b;c.sibling=null}return b.child}function xk(a,b,c){switch(b.tag){case 3:ti(b);Qb();break;case 5:Mh(b);break;case 1:ea(b.type)&&ld(b);break;case 4:jf(b,b.stateNode.containerInfo);break;case 10:var d=b.type._context,e=b.memoizedProps.value;y(rd,d._currentValue);d._currentValue=e;break;case 13:d=b.memoizedState;if(null!==d){if(null!==d.dehydrated)return y(G,G.current& -1),b.flags|=128,null;if(0!==(c&b.child.childLanes))return vi(a,b,c);y(G,G.current&1);a=Qa(a,b,c);return null!==a?a.sibling:null}y(G,G.current&1);break;case 19:d=0!==(c&b.childLanes);if(0!==(a.flags&128)){if(d)return xi(a,b,c);b.flags|=128}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null,e.lastEffect=null);y(G,G.current);if(d)break;else return null;case 22:case 23:return b.lanes=0,qi(a,b,c)}return Qa(a,b,c)}function Dc(a,b){if(!D)switch(a.tailMode){case "hidden":b=a.tail;for(var c=null;null!== +if(3===k||4===k)if(k=g.stateNode.containerInfo,k===e||8===k.nodeType&&k.parentNode===e)return;g=g.return}for(;null!==h;){g=ob(h);if(null===g)return;k=g.tag;if(5===k||6===k){d=f=g;continue a}h=h.parentNode}}d=d.return}wg(function(){var d=f,e=re(c),g=[];a:{var h=fh.get(a);if(void 0!==h){var k=He,m=a;switch(a){case "keypress":if(0===cd(c))break a;case "keydown":case "keyup":k=Vj;break;case "focusin":m="focus";k=Pe;break;case "focusout":m="blur";k=Pe;break;case "beforeblur":case "afterblur":k=Pe;break; +case "click":if(2===c.button)break a;case "auxclick":case "dblclick":case "mousedown":case "mousemove":case "mouseup":case "mouseout":case "mouseover":case "contextmenu":k=ih;break;case "drag":case "dragend":case "dragenter":case "dragexit":case "dragleave":case "dragover":case "dragstart":case "drop":k=Wj;break;case "touchcancel":case "touchend":case "touchmove":case "touchstart":k=Xj;break;case jh:case kh:case lh:k=Yj;break;case mh:k=Zj;break;case "scroll":k=ak;break;case "wheel":k=bk;break;case "copy":case "cut":case "paste":k= +ck;break;case "gotpointercapture":case "lostpointercapture":case "pointercancel":case "pointerdown":case "pointermove":case "pointerout":case "pointerover":case "pointerup":k=nh}var l=0!==(b&4),p=!l&&"scroll"===a,w=l?null!==h?h+"Capture":null:h;l=[];for(var A=d,t;null!==A;){t=A;var M=t.stateNode;5===t.tag&&null!==M&&(t=M,null!==w&&(M=fc(A,w),null!=M&&l.push(tc(A,M,t))));if(p)break;A=A.return}0Mb||(a.current=Se[Mb],Se[Mb]=null,Mb--)} +function y(a,b,c){Mb++;Se[Mb]=a.current;a.current=b}function Nb(a,b){var c=a.type.contextTypes;if(!c)return cb;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function ea(a){a=a.childContextTypes;return null!==a&&void 0!==a}function th(a,b,c){if(J.current!==cb)throw Error(m(168)); +y(J,b);y(S,c)}function uh(a,b,c){var d=a.stateNode;b=b.childContextTypes;if("function"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in b))throw Error(m(108,gj(a)||"Unknown",e));return E({},c,d)}function ld(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||cb;pb=J.current;y(J,a);y(S,S.current);return!0}function vh(a,b,c){var d=a.stateNode;if(!d)throw Error(m(169));c?(a=uh(a,b,pb),d.__reactInternalMemoizedMergedChildContext=a,v(S),v(J),y(J,a)):v(S); +y(S,c)}function wh(a){null===La?La=[a]:La.push(a)}function jk(a){md=!0;wh(a)}function db(){if(!Te&&null!==La){Te=!0;var a=0,b=z;try{var c=La;for(z=1;a>=g;e-=g;Ma=1<<32-ta(b)+e|c<t?(q=l,l=null):q=l.sibling;var A=r(e,l,h[t],k);if(null===A){null===l&&(l=q);break}a&&l&&null===A.alternate&&b(e,l);g=f(A,g,t);null===m?n=A:m.sibling=A;m=A;l=q}if(t===h.length)return c(e,l),D&&qb(e,t),n;if(null===l){for(;t< +h.length;t++)l=u(e,h[t],k),null!==l&&(g=f(l,g,t),null===m?n=l:m.sibling=l,m=l);D&&qb(e,t);return n}for(l=d(e,l);tt?(A=q,q=null):A=q.sibling;var x=r(e,q,w.value,k);if(null===x){null===q&&(q=A);break}a&&q&&null===x.alternate&&b(e,q);g=f(x,g,t);null===l?n=x:l.sibling=x;l=x;q=A}if(w.done)return c(e,q),D&&qb(e,t),n;if(null===q){for(;!w.done;t++,w=h.next())w=u(e,w.value,k),null!==w&&(g=f(w,g,t),null===l?n=w:l.sibling=w,l=w);D&&qb(e,t);return n}for(q=d(e,q);!w.done;t++,w=h.next())w=p(q,e,t,w.value,k),null!==w&&(a&&null!==w.alternate&&q.delete(null===w.key?t:w.key),g=f(w,g,t),null===l?n=w:l.sibling= +w,l=w);a&&q.forEach(function(a){return b(e,a)});D&&qb(e,t);return n}function v(a,d,f,h){"object"===typeof f&&null!==f&&f.type===Bb&&null===f.key&&(f=f.props.children);if("object"===typeof f&&null!==f){switch(f.$$typeof){case sd:a:{for(var k=f.key,n=d;null!==n;){if(n.key===k){k=f.type;if(k===Bb){if(7===n.tag){c(a,n.sibling);d=e(n,f.props.children);d.return=a;a=d;break a}}else if(n.elementType===k||"object"===typeof k&&null!==k&&k.$$typeof===Ta&&Ch(k)===n.type){c(a,n.sibling);d=e(n,f.props);d.ref=vc(a, +n,f);d.return=a;a=d;break a}c(a,n);break}else b(a,n);n=n.sibling}f.type===Bb?(d=sb(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=rd(f.type,f.key,f.props,null,a.mode,h),h.ref=vc(a,d,f),h.return=a,a=h)}return g(a);case Cb:a:{for(n=f.key;null!==d;){if(d.key===n)if(4===d.tag&&d.stateNode.containerInfo===f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=$e(f,a.mode,h);d.return=a; +a=d}return g(a);case Ta:return n=f._init,v(a,d,n(f._payload),h)}if(cc(f))return x(a,d,f,h);if(ac(f))return I(a,d,f,h);qd(a,f)}return"string"===typeof f&&""!==f||"number"===typeof f?(f=""+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d):(c(a,d),d=Ze(f,a.mode,h),d.return=a,a=d),g(a)):c(a,d)}return v}function af(){bf=Rb=td=null}function cf(a,b){b=ud.current;v(ud);a._currentValue=b}function df(a,b,c){for(;null!==a;){var d=a.alternate;(a.childLanes&b)!==b?(a.childLanes|=b,null!==d&&(d.childLanes|= +b)):null!==d&&(d.childLanes&b)!==b&&(d.childLanes|=b);if(a===c)break;a=a.return}}function Sb(a,b){td=a;bf=Rb=null;a=a.dependencies;null!==a&&null!==a.firstContext&&(0!==(a.lanes&b)&&(ha=!0),a.firstContext=null)}function qa(a){var b=a._currentValue;if(bf!==a)if(a={context:a,memoizedValue:b,next:null},null===Rb){if(null===td)throw Error(m(308));Rb=a;td.dependencies={lanes:0,firstContext:a}}else Rb=Rb.next=a;return b}function ef(a){null===tb?tb=[a]:tb.push(a)}function Eh(a,b,c,d){var e=b.interleaved; +null===e?(c.next=c,ef(b)):(c.next=e.next,e.next=c);b.interleaved=c;return Oa(a,d)}function Oa(a,b){a.lanes|=b;var c=a.alternate;null!==c&&(c.lanes|=b);c=a;for(a=a.return;null!==a;)a.childLanes|=b,c=a.alternate,null!==c&&(c.childLanes|=b),c=a,a=a.return;return 3===c.tag?c.stateNode:null}function ff(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Fh(a,b){a=a.updateQueue;b.updateQueue===a&&(b.updateQueue= +{baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,effects:a.effects})}function Pa(a,b){return{eventTime:a,lane:b,tag:0,payload:null,callback:null,next:null}}function fb(a,b,c){var d=a.updateQueue;if(null===d)return null;d=d.shared;if(0!==(p&2)){var e=d.pending;null===e?b.next=b:(b.next=e.next,e.next=b);d.pending=b;return kk(a,c)}e=d.interleaved;null===e?(b.next=b,ef(d)):(b.next=e.next,e.next=b);d.interleaved=b;return Oa(a,c)}function vd(a,b,c){b= +b.updateQueue;if(null!==b&&(b=b.shared,0!==(c&4194240))){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function Gh(a,b){var c=a.updateQueue,d=a.alternate;if(null!==d&&(d=d.updateQueue,c===d)){var e=null,f=null;c=c.firstBaseUpdate;if(null!==c){do{var g={eventTime:c.eventTime,lane:c.lane,tag:c.tag,payload:c.payload,callback:c.callback,next:null};null===f?e=f=g:f=f.next=g;c=c.next}while(null!==c);null===f?e=f=b:f=f.next=b}else e=f=b;c={baseState:d.baseState,firstBaseUpdate:e,lastBaseUpdate:f, +shared:d.shared,effects:d.effects};a.updateQueue=c;return}a=c.lastBaseUpdate;null===a?c.firstBaseUpdate=b:a.next=b;c.lastBaseUpdate=b}function wd(a,b,c,d){var e=a.updateQueue;gb=!1;var f=e.firstBaseUpdate,g=e.lastBaseUpdate,h=e.shared.pending;if(null!==h){e.shared.pending=null;var k=h,n=k.next;k.next=null;null===g?f=n:g.next=n;g=k;var l=a.alternate;null!==l&&(l=l.updateQueue,h=l.lastBaseUpdate,h!==g&&(null===h?l.firstBaseUpdate=n:h.next=n,l.lastBaseUpdate=k))}if(null!==f){var m=e.baseState;g=0;l= +n=k=null;h=f;do{var r=h.lane,p=h.eventTime;if((d&r)===r){null!==l&&(l=l.next={eventTime:p,lane:0,tag:h.tag,payload:h.payload,callback:h.callback,next:null});a:{var x=a,v=h;r=b;p=c;switch(v.tag){case 1:x=v.payload;if("function"===typeof x){m=x.call(p,m,r);break a}m=x;break a;case 3:x.flags=x.flags&-65537|128;case 0:x=v.payload;r="function"===typeof x?x.call(p,m,r):x;if(null===r||void 0===r)break a;m=E({},m,r);break a;case 2:gb=!0}}null!==h.callback&&0!==h.lane&&(a.flags|=64,r=e.effects,null===r?e.effects= +[h]:r.push(h))}else p={eventTime:p,lane:r,tag:h.tag,payload:h.payload,callback:h.callback,next:null},null===l?(n=l=p,k=m):l=l.next=p,g|=r;h=h.next;if(null===h)if(h=e.shared.pending,null===h)break;else r=h,h=r.next,r.next=null,e.lastBaseUpdate=r,e.shared.pending=null}while(1);null===l&&(k=m);e.baseState=k;e.firstBaseUpdate=n;e.lastBaseUpdate=l;b=e.shared.interleaved;if(null!==b){e=b;do g|=e.lane,e=e.next;while(e!==b)}else null===f&&(e.shared.lanes=0);ra|=g;a.lanes=g;a.memoizedState=m}}function Hh(a, +b,c){a=b.effects;b.effects=null;if(null!==a)for(b=0;bc?c:4;a(!0);var d=sf.transition;sf.transition= +{};try{a(!1),b()}finally{z=c,sf.transition=d}}function $h(){return sa().memoizedState}function qk(a,b,c){var d=hb(a);c={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ai(a))bi(b,c);else if(c=Eh(a,b,c,d),null!==c){var e=Z();xa(c,a,d,e);ci(c,b,d)}}function ok(a,b,c){var d=hb(a),e={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ai(a))bi(b,e);else{var f=a.alternate;if(0===a.lanes&&(null===f||0===f.lanes)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState, +h=f(g,c);e.hasEagerState=!0;e.eagerState=h;if(ua(h,g)){var k=b.interleaved;null===k?(e.next=e,ef(b)):(e.next=k.next,k.next=e);b.interleaved=e;return}}catch(n){}finally{}c=Eh(a,b,e,d);null!==c&&(e=Z(),xa(c,a,d,e),ci(c,b,d))}}function ai(a){var b=a.alternate;return a===C||null!==b&&b===C}function bi(a,b){zc=Ad=!0;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}function ci(a,b,c){if(0!==(c&4194240)){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function ya(a,b){if(a&& +a.defaultProps){b=E({},b);a=a.defaultProps;for(var c in a)void 0===b[c]&&(b[c]=a[c]);return b}return b}function tf(a,b,c,d){b=a.memoizedState;c=c(d,b);c=null===c||void 0===c?b:E({},b,c);a.memoizedState=c;0===a.lanes&&(a.updateQueue.baseState=c)}function di(a,b,c,d,e,f,g){a=a.stateNode;return"function"===typeof a.shouldComponentUpdate?a.shouldComponentUpdate(d,f,g):b.prototype&&b.prototype.isPureReactComponent?!qc(c,d)||!qc(e,f):!0}function ei(a,b,c){var d=!1,e=cb;var f=b.contextType;"object"===typeof f&& +null!==f?f=qa(f):(e=ea(b)?pb:J.current,d=b.contextTypes,f=(d=null!==d&&void 0!==d)?Nb(a,e):cb);b=new b(c,f);a.memoizedState=null!==b.state&&void 0!==b.state?b.state:null;b.updater=Dd;a.stateNode=b;b._reactInternals=a;d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=e,a.__reactInternalMemoizedMaskedChildContext=f);return b}function fi(a,b,c,d){a=b.state;"function"===typeof b.componentWillReceiveProps&&b.componentWillReceiveProps(c,d);"function"===typeof b.UNSAFE_componentWillReceiveProps&& +b.UNSAFE_componentWillReceiveProps(c,d);b.state!==a&&Dd.enqueueReplaceState(b,b.state,null)}function uf(a,b,c,d){var e=a.stateNode;e.props=c;e.state=a.memoizedState;e.refs={};ff(a);var f=b.contextType;"object"===typeof f&&null!==f?e.context=qa(f):(f=ea(b)?pb:J.current,e.context=Nb(a,f));e.state=a.memoizedState;f=b.getDerivedStateFromProps;"function"===typeof f&&(tf(a,b,f,c),e.state=a.memoizedState);"function"===typeof b.getDerivedStateFromProps||"function"===typeof e.getSnapshotBeforeUpdate||"function"!== +typeof e.UNSAFE_componentWillMount&&"function"!==typeof e.componentWillMount||(b=e.state,"function"===typeof e.componentWillMount&&e.componentWillMount(),"function"===typeof e.UNSAFE_componentWillMount&&e.UNSAFE_componentWillMount(),b!==e.state&&Dd.enqueueReplaceState(e,e.state,null),wd(a,c,e,d),e.state=a.memoizedState);"function"===typeof e.componentDidMount&&(a.flags|=4194308)}function Ub(a,b){try{var c="",d=b;do c+=fj(d),d=d.return;while(d);var e=c}catch(f){e="\nError generating stack: "+f.message+ +"\n"+f.stack}return{value:a,source:b,stack:e,digest:null}}function vf(a,b,c){return{value:a,source:null,stack:null!=c?c:null,digest:null!=b?b:null}}function wf(a,b){try{console.error(b.value)}catch(c){setTimeout(function(){throw c;})}}function gi(a,b,c){c=Pa(-1,c);c.tag=3;c.payload={element:null};var d=b.value;c.callback=function(){Ed||(Ed=!0,xf=d);wf(a,b)};return c}function hi(a,b,c){c=Pa(-1,c);c.tag=3;var d=a.type.getDerivedStateFromError;if("function"===typeof d){var e=b.value;c.payload=function(){return d(e)}; +c.callback=function(){wf(a,b)}}var f=a.stateNode;null!==f&&"function"===typeof f.componentDidCatch&&(c.callback=function(){wf(a,b);"function"!==typeof d&&(null===ib?ib=new Set([this]):ib.add(this));var c=b.stack;this.componentDidCatch(b.value,{componentStack:null!==c?c:""})});return c}function ii(a,b,c){var d=a.pingCache;if(null===d){d=a.pingCache=new rk;var e=new Set;d.set(b,e)}else e=d.get(b),void 0===e&&(e=new Set,d.set(b,e));e.has(c)||(e.add(c),a=sk.bind(null,a,b,c),b.then(a,a))}function ji(a){do{var b; +if(b=13===a.tag)b=a.memoizedState,b=null!==b?null!==b.dehydrated?!0:!1:!0;if(b)return a;a=a.return}while(null!==a);return null}function ki(a,b,c,d,e){if(0===(a.mode&1))return a===b?a.flags|=65536:(a.flags|=128,c.flags|=131072,c.flags&=-52805,1===c.tag&&(null===c.alternate?c.tag=17:(b=Pa(-1,1),b.tag=2,fb(c,b,1))),c.lanes|=1),a;a.flags|=65536;a.lanes=e;return a}function aa(a,b,c,d){b.child=null===a?li(b,null,c,d):Vb(b,a.child,c,d)}function mi(a,b,c,d,e){c=c.render;var f=b.ref;Sb(b,e);d=mf(a,b,c,d,f, +e);c=nf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&c&&Ue(b);b.flags|=1;aa(a,b,d,e);return b.child}function ni(a,b,c,d,e){if(null===a){var f=c.type;if("function"===typeof f&&!yf(f)&&void 0===f.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=f,oi(a,b,f,d,e);a=rd(c.type,null,d,b,b.mode,e);a.ref=b.ref;a.return=b;return b.child=a}f=a.child;if(0===(a.lanes&e)){var g=f.memoizedProps;c=c.compare;c=null!==c?c:qc;if(c(g,d)&&a.ref=== +b.ref)return Qa(a,b,e)}b.flags|=1;a=eb(f,d);a.ref=b.ref;a.return=b;return b.child=a}function oi(a,b,c,d,e){if(null!==a){var f=a.memoizedProps;if(qc(f,d)&&a.ref===b.ref)if(ha=!1,b.pendingProps=d=f,0!==(a.lanes&e))0!==(a.flags&131072)&&(ha=!0);else return b.lanes=a.lanes,Qa(a,b,e)}return zf(a,b,c,d,e)}function pi(a,b,c){var d=b.pendingProps,e=d.children,f=null!==a?a.memoizedState:null;if("hidden"===d.mode)if(0===(b.mode&1))b.memoizedState={baseLanes:0,cachePool:null,transitions:null},y(Ga,ba),ba|=c; +else{if(0===(c&1073741824))return a=null!==f?f.baseLanes|c:c,b.lanes=b.childLanes=1073741824,b.memoizedState={baseLanes:a,cachePool:null,transitions:null},b.updateQueue=null,y(Ga,ba),ba|=a,null;b.memoizedState={baseLanes:0,cachePool:null,transitions:null};d=null!==f?f.baseLanes:c;y(Ga,ba);ba|=d}else null!==f?(d=f.baseLanes|c,b.memoizedState=null):d=c,y(Ga,ba),ba|=d;aa(a,b,e,c);return b.child}function qi(a,b){var c=b.ref;if(null===a&&null!==c||null!==a&&a.ref!==c)b.flags|=512,b.flags|=2097152}function zf(a, +b,c,d,e){var f=ea(c)?pb:J.current;f=Nb(b,f);Sb(b,e);c=mf(a,b,c,d,f,e);d=nf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&d&&Ue(b);b.flags|=1;aa(a,b,c,e);return b.child}function ri(a,b,c,d,e){if(ea(c)){var f=!0;ld(b)}else f=!1;Sb(b,e);if(null===b.stateNode)Fd(a,b),ei(b,c,d),uf(b,c,d,e),d=!0;else if(null===a){var g=b.stateNode,h=b.memoizedProps;g.props=h;var k=g.context,n=c.contextType;"object"===typeof n&&null!==n?n=qa(n):(n=ea(c)?pb:J.current,n=Nb(b, +n));var l=c.getDerivedStateFromProps,m="function"===typeof l||"function"===typeof g.getSnapshotBeforeUpdate;m||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||k!==n)&&fi(b,g,d,n);gb=!1;var r=b.memoizedState;g.state=r;wd(b,d,g,e);k=b.memoizedState;h!==d||r!==k||S.current||gb?("function"===typeof l&&(tf(b,c,l,d),k=b.memoizedState),(h=gb||di(b,c,h,d,r,k,n))?(m||"function"!==typeof g.UNSAFE_componentWillMount&&"function"!==typeof g.componentWillMount|| +("function"===typeof g.componentWillMount&&g.componentWillMount(),"function"===typeof g.UNSAFE_componentWillMount&&g.UNSAFE_componentWillMount()),"function"===typeof g.componentDidMount&&(b.flags|=4194308)):("function"===typeof g.componentDidMount&&(b.flags|=4194308),b.memoizedProps=d,b.memoizedState=k),g.props=d,g.state=k,g.context=n,d=h):("function"===typeof g.componentDidMount&&(b.flags|=4194308),d=!1)}else{g=b.stateNode;Fh(a,b);h=b.memoizedProps;n=b.type===b.elementType?h:ya(b.type,h);g.props= +n;m=b.pendingProps;r=g.context;k=c.contextType;"object"===typeof k&&null!==k?k=qa(k):(k=ea(c)?pb:J.current,k=Nb(b,k));var p=c.getDerivedStateFromProps;(l="function"===typeof p||"function"===typeof g.getSnapshotBeforeUpdate)||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==m||r!==k)&&fi(b,g,d,k);gb=!1;r=b.memoizedState;g.state=r;wd(b,d,g,e);var x=b.memoizedState;h!==m||r!==x||S.current||gb?("function"===typeof p&&(tf(b,c,p,d),x=b.memoizedState), +(n=gb||di(b,c,n,d,r,x,k)||!1)?(l||"function"!==typeof g.UNSAFE_componentWillUpdate&&"function"!==typeof g.componentWillUpdate||("function"===typeof g.componentWillUpdate&&g.componentWillUpdate(d,x,k),"function"===typeof g.UNSAFE_componentWillUpdate&&g.UNSAFE_componentWillUpdate(d,x,k)),"function"===typeof g.componentDidUpdate&&(b.flags|=4),"function"===typeof g.getSnapshotBeforeUpdate&&(b.flags|=1024)):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|= +4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),b.memoizedProps=d,b.memoizedState=x),g.props=d,g.state=x,g.context=k,d=n):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),d=!1)}return Af(a,b,c,d,f,e)}function Af(a,b,c,d,e,f){qi(a,b);var g=0!==(b.flags&128);if(!d&&!g)return e&&vh(b,c,!1), +Qa(a,b,f);d=b.stateNode;tk.current=b;var h=g&&"function"!==typeof c.getDerivedStateFromError?null:d.render();b.flags|=1;null!==a&&g?(b.child=Vb(b,a.child,null,f),b.child=Vb(b,null,h,f)):aa(a,b,h,f);b.memoizedState=d.state;e&&vh(b,c,!0);return b.child}function si(a){var b=a.stateNode;b.pendingContext?th(a,b.pendingContext,b.pendingContext!==b.context):b.context&&th(a,b.context,!1);gf(a,b.containerInfo)}function ti(a,b,c,d,e){Qb();Ye(e);b.flags|=256;aa(a,b,c,d);return b.child}function Bf(a){return{baseLanes:a, +cachePool:null,transitions:null}}function ui(a,b,c){var d=b.pendingProps,e=F.current,f=!1,g=0!==(b.flags&128),h;(h=g)||(h=null!==a&&null===a.memoizedState?!1:0!==(e&2));if(h)f=!0,b.flags&=-129;else if(null===a||null!==a.memoizedState)e|=1;y(F,e&1);if(null===a){Xe(b);a=b.memoizedState;if(null!==a&&(a=a.dehydrated,null!==a))return 0===(b.mode&1)?b.lanes=1:"$!"===a.data?b.lanes=8:b.lanes=1073741824,null;g=d.children;a=d.fallback;return f?(d=b.mode,f=b.child,g={mode:"hidden",children:g},0===(d&1)&&null!== +f?(f.childLanes=0,f.pendingProps=g):f=Gd(g,d,0,null),a=sb(a,d,c,null),f.return=b,a.return=b,f.sibling=a,b.child=f,b.child.memoizedState=Bf(c),b.memoizedState=Cf,a):Df(b,g)}e=a.memoizedState;if(null!==e&&(h=e.dehydrated,null!==h))return uk(a,b,g,d,h,e,c);if(f){f=d.fallback;g=b.mode;e=a.child;h=e.sibling;var k={mode:"hidden",children:d.children};0===(g&1)&&b.child!==e?(d=b.child,d.childLanes=0,d.pendingProps=k,b.deletions=null):(d=eb(e,k),d.subtreeFlags=e.subtreeFlags&14680064);null!==h?f=eb(h,f):(f= +sb(f,g,c,null),f.flags|=2);f.return=b;d.return=b;d.sibling=f;b.child=d;d=f;f=b.child;g=a.child.memoizedState;g=null===g?Bf(c):{baseLanes:g.baseLanes|c,cachePool:null,transitions:g.transitions};f.memoizedState=g;f.childLanes=a.childLanes&~c;b.memoizedState=Cf;return d}f=a.child;a=f.sibling;d=eb(f,{mode:"visible",children:d.children});0===(b.mode&1)&&(d.lanes=c);d.return=b;d.sibling=null;null!==a&&(c=b.deletions,null===c?(b.deletions=[a],b.flags|=16):c.push(a));b.child=d;b.memoizedState=null;return d} +function Df(a,b,c){b=Gd({mode:"visible",children:b},a.mode,0,null);b.return=a;return a.child=b}function Hd(a,b,c,d){null!==d&&Ye(d);Vb(b,a.child,null,c);a=Df(b,b.pendingProps.children);a.flags|=2;b.memoizedState=null;return a}function uk(a,b,c,d,e,f,g){if(c){if(b.flags&256)return b.flags&=-257,d=vf(Error(m(422))),Hd(a,b,g,d);if(null!==b.memoizedState)return b.child=a.child,b.flags|=128,null;f=d.fallback;e=b.mode;d=Gd({mode:"visible",children:d.children},e,0,null);f=sb(f,e,g,null);f.flags|=2;d.return= +b;f.return=b;d.sibling=f;b.child=d;0!==(b.mode&1)&&Vb(b,a.child,null,g);b.child.memoizedState=Bf(g);b.memoizedState=Cf;return f}if(0===(b.mode&1))return Hd(a,b,g,null);if("$!"===e.data){d=e.nextSibling&&e.nextSibling.dataset;if(d)var h=d.dgst;d=h;f=Error(m(419));d=vf(f,d,void 0);return Hd(a,b,g,d)}h=0!==(g&a.childLanes);if(ha||h){d=O;if(null!==d){switch(g&-g){case 4:e=2;break;case 16:e=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:e= +32;break;case 536870912:e=268435456;break;default:e=0}e=0!==(e&(d.suspendedLanes|g))?0:e;0!==e&&e!==f.retryLane&&(f.retryLane=e,Oa(a,e),xa(d,a,e,-1))}Ef();d=vf(Error(m(421)));return Hd(a,b,g,d)}if("$?"===e.data)return b.flags|=128,b.child=a.child,b=vk.bind(null,a),e._reactRetry=b,null;a=f.treeContext;fa=Ka(e.nextSibling);la=b;D=!0;wa=null;null!==a&&(na[oa++]=Ma,na[oa++]=Na,na[oa++]=rb,Ma=a.id,Na=a.overflow,rb=b);b=Df(b,d.children);b.flags|=4096;return b}function vi(a,b,c){a.lanes|=b;var d=a.alternate; +null!==d&&(d.lanes|=b);df(a.return,b,c)}function Ff(a,b,c,d,e){var f=a.memoizedState;null===f?a.memoizedState={isBackwards:b,rendering:null,renderingStartTime:0,last:d,tail:c,tailMode:e}:(f.isBackwards=b,f.rendering=null,f.renderingStartTime=0,f.last=d,f.tail=c,f.tailMode=e)}function wi(a,b,c){var d=b.pendingProps,e=d.revealOrder,f=d.tail;aa(a,b,d.children,c);d=F.current;if(0!==(d&2))d=d&1|2,b.flags|=128;else{if(null!==a&&0!==(a.flags&128))a:for(a=b.child;null!==a;){if(13===a.tag)null!==a.memoizedState&& +vi(a,c,b);else if(19===a.tag)vi(a,c,b);else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===b)break a;for(;null===a.sibling;){if(null===a.return||a.return===b)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}d&=1}y(F,d);if(0===(b.mode&1))b.memoizedState=null;else switch(e){case "forwards":c=b.child;for(e=null;null!==c;)a=c.alternate,null!==a&&null===xd(a)&&(e=c),c=c.sibling;c=e;null===c?(e=b.child,b.child=null):(e=c.sibling,c.sibling=null);Ff(b,!1,e,c,f);break;case "backwards":c= +null;e=b.child;for(b.child=null;null!==e;){a=e.alternate;if(null!==a&&null===xd(a)){b.child=e;break}a=e.sibling;e.sibling=c;c=e;e=a}Ff(b,!0,c,null,f);break;case "together":Ff(b,!1,null,null,void 0);break;default:b.memoizedState=null}return b.child}function Fd(a,b){0===(b.mode&1)&&null!==a&&(a.alternate=null,b.alternate=null,b.flags|=2)}function Qa(a,b,c){null!==a&&(b.dependencies=a.dependencies);ra|=b.lanes;if(0===(c&b.childLanes))return null;if(null!==a&&b.child!==a.child)throw Error(m(153));if(null!== +b.child){a=b.child;c=eb(a,a.pendingProps);b.child=c;for(c.return=b;null!==a.sibling;)a=a.sibling,c=c.sibling=eb(a,a.pendingProps),c.return=b;c.sibling=null}return b.child}function wk(a,b,c){switch(b.tag){case 3:si(b);Qb();break;case 5:Ih(b);break;case 1:ea(b.type)&&ld(b);break;case 4:gf(b,b.stateNode.containerInfo);break;case 10:var d=b.type._context,e=b.memoizedProps.value;y(ud,d._currentValue);d._currentValue=e;break;case 13:d=b.memoizedState;if(null!==d){if(null!==d.dehydrated)return y(F,F.current& +1),b.flags|=128,null;if(0!==(c&b.child.childLanes))return ui(a,b,c);y(F,F.current&1);a=Qa(a,b,c);return null!==a?a.sibling:null}y(F,F.current&1);break;case 19:d=0!==(c&b.childLanes);if(0!==(a.flags&128)){if(d)return wi(a,b,c);b.flags|=128}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null,e.lastEffect=null);y(F,F.current);if(d)break;else return null;case 22:case 23:return b.lanes=0,pi(a,b,c)}return Qa(a,b,c)}function Dc(a,b){if(!D)switch(a.tailMode){case "hidden":b=a.tail;for(var c=null;null!== b;)null!==b.alternate&&(c=b),b=b.sibling;null===c?a.tail=null:c.sibling=null;break;case "collapsed":c=a.tail;for(var d=null;null!==c;)null!==c.alternate&&(d=c),c=c.sibling;null===d?b||null===a.tail?a.tail=null:a.tail.sibling=null:d.sibling=null}}function W(a){var b=null!==a.alternate&&a.alternate.child===a.child,c=0,d=0;if(b)for(var e=a.child;null!==e;)c|=e.lanes|e.childLanes,d|=e.subtreeFlags&14680064,d|=e.flags&14680064,e.return=a,e=e.sibling;else for(e=a.child;null!==e;)c|=e.lanes|e.childLanes, -d|=e.subtreeFlags,d|=e.flags,e.return=a,e=e.sibling;a.subtreeFlags|=d;a.childLanes=c;return b}function yk(a,b,c){var d=b.pendingProps;Ve(b);switch(b.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return W(b),null;case 1:return ea(b.type)&&(w(S),w(J)),W(b),null;case 3:d=b.stateNode;Tb();w(S);w(J);lf();d.pendingContext&&(d.context=d.pendingContext,d.pendingContext=null);if(null===a||null===a.child)pd(b)?b.flags|=4:null===a||a.memoizedState.isDehydrated&&0===(b.flags& -256)||(b.flags|=1024,null!==wa&&(Gf(wa),wa=null));yi(a,b);W(b);return null;case 5:kf(b);var e=vb(xc.current);c=b.type;if(null!==a&&null!=b.stateNode)zk(a,b,c,d,e),a.ref!==b.ref&&(b.flags|=512,b.flags|=2097152);else{if(!d){if(null===b.stateNode)throw Error(n(166));W(b);return null}a=vb(Ea.current);if(pd(b)){d=b.stateNode;c=b.type;var f=b.memoizedProps;d[Da]=b;d[uc]=f;a=0!==(b.mode&1);switch(c){case "dialog":B("cancel",d);B("close",d);break;case "iframe":case "object":case "embed":B("load",d);break; +d|=e.subtreeFlags,d|=e.flags,e.return=a,e=e.sibling;a.subtreeFlags|=d;a.childLanes=c;return b}function xk(a,b,c){var d=b.pendingProps;Ve(b);switch(b.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return W(b),null;case 1:return ea(b.type)&&(v(S),v(J)),W(b),null;case 3:d=b.stateNode;Tb();v(S);v(J);jf();d.pendingContext&&(d.context=d.pendingContext,d.pendingContext=null);if(null===a||null===a.child)pd(b)?b.flags|=4:null===a||a.memoizedState.isDehydrated&&0===(b.flags& +256)||(b.flags|=1024,null!==wa&&(Gf(wa),wa=null));xi(a,b);W(b);return null;case 5:hf(b);var e=ub(xc.current);c=b.type;if(null!==a&&null!=b.stateNode)yk(a,b,c,d,e),a.ref!==b.ref&&(b.flags|=512,b.flags|=2097152);else{if(!d){if(null===b.stateNode)throw Error(m(166));W(b);return null}a=ub(Ea.current);if(pd(b)){d=b.stateNode;c=b.type;var f=b.memoizedProps;d[Da]=b;d[uc]=f;a=0!==(b.mode&1);switch(c){case "dialog":B("cancel",d);B("close",d);break;case "iframe":case "object":case "embed":B("load",d);break; case "video":case "audio":for(e=0;e\x3c/script>",a=a.removeChild(a.firstChild)):"string"===typeof d.is?a=g.createElement(c,{is:d.is}):(a=g.createElement(c),"select"===c&&(g=a,d.multiple?g.multiple=!0:d.size&&(g.size=d.size))):a=g.createElementNS(a,c);a[Da]=b;a[uc]=d;Ak(a,b,!1,!1);b.stateNode=a;a:{g=qe(c,d);switch(c){case "dialog":B("cancel",a);B("close",a);e=d;break;case "iframe":case "object":case "embed":B("load",a);e=d;break; +a&&(a=qg(c));"http://www.w3.org/1999/xhtml"===a?"script"===c?(a=g.createElement("div"),a.innerHTML=" {% django_htmx_script %} diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index 940f74346..397553446 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -188,12 +188,9 @@ Blocks: {# Footer text #}

    {# /Footer text #} diff --git a/netbox/templates/core/system.html b/netbox/templates/core/system.html index 320038ac6..4d133ca53 100644 --- a/netbox/templates/core/system.html +++ b/netbox/templates/core/system.html @@ -28,8 +28,13 @@
    {% trans "System Status" %}
    - - + + diff --git a/netbox/utilities/error_handlers.py b/netbox/utilities/error_handlers.py index 89e3a967d..66e74739a 100644 --- a/netbox/utilities/error_handlers.py +++ b/netbox/utilities/error_handlers.py @@ -53,7 +53,7 @@ def handle_rest_api_exception(request, *args, **kwargs): data = { 'error': str(error), 'exception': type_.__name__, - 'netbox_version': settings.VERSION, + 'netbox_version': settings.RELEASE.full_version, 'python_version': platform.python_version(), } return JsonResponse(data, status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/netbox/utilities/release.py b/netbox/utilities/release.py new file mode 100644 index 000000000..dacec1010 --- /dev/null +++ b/netbox/utilities/release.py @@ -0,0 +1,57 @@ +import datetime +import os +import yaml +from dataclasses import dataclass +from typing import Union + +from django.core.exceptions import ImproperlyConfigured + +RELEASE_PATH = 'release.yaml' +LOCAL_RELEASE_PATH = 'local/release.yaml' + + +@dataclass +class ReleaseInfo: + version: str + edition: str = 'Community' + published: Union[datetime.date, None] = None + designation: Union[str, None] = None + + @property + def full_version(self): + if self.designation: + return f"{self.version}-{self.designation}" + return self.version + + @property + def name(self): + return f"NetBox {self.edition} v{self.full_version}" + + +def load_release_data(): + """ + Load any locally-defined release attributes and return a ReleaseInfo instance. + """ + base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + # Load canonical release attributes + with open(os.path.join(base_path, RELEASE_PATH), 'r') as release_file: + data = yaml.safe_load(release_file) + + # Overlay any local release date (if defined) + try: + with open(os.path.join(base_path, LOCAL_RELEASE_PATH), 'r') as release_file: + local_data = yaml.safe_load(release_file) + except FileNotFoundError: + local_data = {} + if type(local_data) is not dict: + raise ImproperlyConfigured( + f"{LOCAL_RELEASE_PATH}: Local release data must be defined as a dictionary." + ) + data.update(local_data) + + # Convert the published date to a date object + if 'published' in data: + data['published'] = datetime.date.fromisoformat(data['published']) + + return ReleaseInfo(**data) From c7176e86fc92e0488e08a6e53b840012ae9d7cdd Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Fri, 7 Jun 2024 11:08:30 -0700 Subject: [PATCH 300/303] 15410 removed deprecated filters (#16410) * 15410 removed legacy filters * 15410 fix tests * 15410 fix tests --- netbox/dcim/filtersets.py | 6 ------ netbox/dcim/tests/test_filtersets.py | 8 ++++---- netbox/extras/filtersets.py | 4 ---- netbox/ipam/filtersets.py | 8 -------- netbox/ipam/tests/test_filtersets.py | 8 ++++---- netbox/vpn/filtersets.py | 8 -------- 6 files changed, 8 insertions(+), 34 deletions(-) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 2fb1e9949..a4d75654e 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -698,9 +698,6 @@ class DeviceTypeComponentFilterSet(django_filters.FilterSet): label=_('Device type (ID)'), ) - # TODO: Remove in v4.1 - devicetype_id = device_type_id - def search(self, queryset, name, value): if not value.strip(): return queryset @@ -717,9 +714,6 @@ class ModularDeviceTypeComponentFilterSet(DeviceTypeComponentFilterSet): label=_('Module type (ID)'), ) - # TODO: Remove in v4.1 - moduletype_id = module_type_id - class ConsolePortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet): diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index 0a22f5a82..df0dc7c7e 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -50,9 +50,9 @@ class DeviceComponentTemplateFilterSetTests: params = {'description': ['foobar1', 'foobar2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) - def test_devicetype_id(self): + def test_device_type_id(self): device_types = DeviceType.objects.all()[:2] - params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]} + params = {'device_type_id': [device_types[0].pk, device_types[1].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) @@ -1753,9 +1753,9 @@ class InventoryItemTemplateTestCase(TestCase, DeviceComponentTemplateFilterSetTe params = {'name': ['Inventory Item 1', 'Inventory Item 2']} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) - def test_devicetype_id(self): + def test_device_type_id(self): device_types = DeviceType.objects.all()[:2] - params = {'devicetype_id': [device_types[0].pk, device_types[1].pk]} + params = {'device_type_id': [device_types[0].pk, device_types[1].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4) def test_label(self): diff --git a/netbox/extras/filtersets.py b/netbox/extras/filtersets.py index 274834d31..c3ac3e6ab 100644 --- a/netbox/extras/filtersets.py +++ b/netbox/extras/filtersets.py @@ -589,10 +589,6 @@ class ConfigContextFilterSet(ChangeLoggedModelFilterSet): label=_('Data file (ID)'), ) - # TODO: Remove in v4.1 - role = device_role - role_id = device_role_id - class Meta: model = ConfigContext fields = ('id', 'name', 'is_active', 'description', 'weight', 'auto_sync_enabled', 'data_synced') diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py index d58f5bfc9..5cdfac34e 100644 --- a/netbox/ipam/filtersets.py +++ b/netbox/ipam/filtersets.py @@ -912,10 +912,6 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet): method='filter_scope' ) - # TODO: Remove in v4.1 - sitegroup = site_group - clustergroup = cluster_group - class Meta: model = VLANGroup fields = ('id', 'name', 'slug', 'min_vid', 'max_vid', 'description', 'scope_id') @@ -1106,10 +1102,6 @@ class ServiceFilterSet(NetBoxModelFilterSet): lookup_expr='contains' ) - # TODO: Remove in v4.1 - ipaddress = ip_address - ipaddress_id = ip_address_id - class Meta: model = Service fields = ('id', 'name', 'protocol', 'description') diff --git a/netbox/ipam/tests/test_filtersets.py b/netbox/ipam/tests/test_filtersets.py index 3a46423a5..8f07a241a 100644 --- a/netbox/ipam/tests/test_filtersets.py +++ b/netbox/ipam/tests/test_filtersets.py @@ -1525,8 +1525,8 @@ class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'region': Region.objects.first().pk} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) - def test_sitegroup(self): - params = {'sitegroup': SiteGroup.objects.first().pk} + def test_site_group(self): + params = {'site_group': SiteGroup.objects.first().pk} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) def test_site(self): @@ -1541,8 +1541,8 @@ class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'rack': Rack.objects.first().pk} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) - def test_clustergroup(self): - params = {'clustergroup': ClusterGroup.objects.first().pk} + def test_cluster_group(self): + params = {'cluster_group': ClusterGroup.objects.first().pk} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) def test_cluster(self): diff --git a/netbox/vpn/filtersets.py b/netbox/vpn/filtersets.py index 3f8b2ee78..92aa702c6 100644 --- a/netbox/vpn/filtersets.py +++ b/netbox/vpn/filtersets.py @@ -190,10 +190,6 @@ class IKEPolicyFilterSet(NetBoxModelFilterSet): to_field_name='name' ) - # TODO: Remove in v4.1 - proposal = ike_proposal - proposal_id = ike_proposal_id - class Meta: model = IKEPolicy fields = ('id', 'name', 'preshared_key', 'description') @@ -255,10 +251,6 @@ class IPSecPolicyFilterSet(NetBoxModelFilterSet): to_field_name='name' ) - # TODO: Remove in v4.1 - proposal = ipsec_proposal - proposal_id = ipsec_proposal_id - class Meta: model = IPSecPolicy fields = ('id', 'name', 'description') From 4da5bd6ed1bbfee5f2f76ed53cd41a048edf4fdc Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 7 Jun 2024 14:36:41 -0400 Subject: [PATCH 301/303] #15908: Update upgrade.sh --- upgrade.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upgrade.sh b/upgrade.sh index 0b2cd383b..512bda34b 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -8,7 +8,7 @@ cd "$(dirname "$0")" -NETBOX_VERSION="$(grep ^VERSION netbox/netbox/settings.py | cut -d\' -f2)" +NETBOX_VERSION="$(grep ^version netbox/release.yaml | cut -d \" -f2)" echo "You are installing (or upgrading to) NetBox version ${NETBOX_VERSION}" VIRTUALENV="$(pwd -P)/venv" From c1d7696b2a0c22c142122d9cb65e5b1c868a08cb Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Tue, 11 Jun 2024 06:19:57 -0700 Subject: [PATCH 302/303] 14692 Convert VM disk size (#16434) * 14692 convert disk size to MB * 14692 fix list display * 14692 fix migration * Update netbox/virtualization/migrations/0039_convert_disk_size.py Co-authored-by: Jeremy Stretch --------- Co-authored-by: Jeremy Stretch --- .../virtualization/virtualmachine.html | 2 +- .../migrations/0039_convert_disk_size.py | 23 +++++++++++++++++++ .../virtualization/models/virtualmachines.py | 2 +- .../virtualization/tables/virtualmachines.py | 7 ++++++ 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 netbox/virtualization/migrations/0039_convert_disk_size.py diff --git a/netbox/templates/virtualization/virtualmachine.html b/netbox/templates/virtualization/virtualmachine.html index ed8980f5c..d2603ddd2 100644 --- a/netbox/templates/virtualization/virtualmachine.html +++ b/netbox/templates/virtualization/virtualmachine.html @@ -137,7 +137,7 @@ + + + +
    {% trans "NetBox version" %}{{ stats.netbox_version }}{% trans "NetBox release" %} + {{ stats.netbox_release.name }} + {% if stats.netbox_release.published %} + ({{ stats.netbox_release.published|isodate }}) + {% endif %} +
    {% trans "Python version" %} {% if object.disk %} - {{ object.disk }} {% trans "GB" context "Abbreviation for gigabyte" %} + {{ object.disk|humanize_megabytes }} {% else %} {{ ''|placeholder }} {% endif %} diff --git a/netbox/virtualization/migrations/0039_convert_disk_size.py b/netbox/virtualization/migrations/0039_convert_disk_size.py new file mode 100644 index 000000000..5bd6c13de --- /dev/null +++ b/netbox/virtualization/migrations/0039_convert_disk_size.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.6 on 2024-06-06 17:46 + +from django.db import migrations +from django.db.models import F + + +def convert_disk_size(apps, schema_editor): + VirtualMachine = apps.get_model('virtualization', 'VirtualMachine') + VirtualMachine.objects.filter(disk__isnull=False).update(disk=F('disk') * 1000) + + +class Migration(migrations.Migration): + + dependencies = [ + ('virtualization', '0038_virtualdisk'), + ] + + operations = [ + migrations.RunPython( + code=convert_disk_size, + reverse_code=migrations.RunPython.noop + ), + ] diff --git a/netbox/virtualization/models/virtualmachines.py b/netbox/virtualization/models/virtualmachines.py index 92f1a9472..fc446922b 100644 --- a/netbox/virtualization/models/virtualmachines.py +++ b/netbox/virtualization/models/virtualmachines.py @@ -125,7 +125,7 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co disk = models.PositiveIntegerField( blank=True, null=True, - verbose_name=_('disk (GB)') + verbose_name=_('disk (MB)') ) # Counter fields diff --git a/netbox/virtualization/tables/virtualmachines.py b/netbox/virtualization/tables/virtualmachines.py index 9d194d268..f37a1bebc 100644 --- a/netbox/virtualization/tables/virtualmachines.py +++ b/netbox/virtualization/tables/virtualmachines.py @@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _ from dcim.tables.devices import BaseInterfaceTable from netbox.tables import NetBoxTable, columns from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin +from utilities.templatetags.helpers import humanize_megabytes from virtualization.models import VirtualDisk, VirtualMachine, VMInterface __all__ = ( @@ -106,6 +107,9 @@ class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable) verbose_name=_('Config Template'), linkify=True ) + disk = tables.Column( + verbose_name=_('Disk'), + ) class Meta(NetBoxTable.Meta): model = VirtualMachine @@ -118,6 +122,9 @@ class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable) 'pk', 'name', 'status', 'site', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip', ) + def render_disk(self, value): + return humanize_megabytes(value) + # # VM components From c6553c45dd1847baef21f2f13bd7ece865293eb9 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Wed, 12 Jun 2024 07:15:16 -0700 Subject: [PATCH 303/303] 7537 add serial number to virtual machines (#16407) * 7537 add serial number to virtual machines * 7537 add migration * 7537 add sn to search * 7537 add to model documentation * 8984 move serializer field * 8984 add to detail view and search index * 7537 serial_number -> serial * 7537 fix migration * Add missing serial field * Give serial field higher precedence for search --------- Co-authored-by: Jeremy Stretch --- docs/models/virtualization/virtualmachine.md | 5 +++++ .../virtualization/virtualmachine.html | 4 ++++ .../api/serializers_/virtualmachines.py | 12 ++++++------ netbox/virtualization/filtersets.py | 8 ++++++-- netbox/virtualization/forms/bulk_import.py | 2 +- netbox/virtualization/forms/filtersets.py | 6 +++++- netbox/virtualization/forms/model_forms.py | 6 +++--- .../0040_virtualmachine_serial_number.py | 18 ++++++++++++++++++ .../virtualization/models/virtualmachines.py | 5 +++++ netbox/virtualization/search.py | 3 ++- .../virtualization/tables/virtualmachines.py | 2 +- netbox/virtualization/tests/test_filtersets.py | 10 ++++++++-- netbox/virtualization/tests/test_views.py | 1 + 13 files changed, 65 insertions(+), 17 deletions(-) create mode 100644 netbox/virtualization/migrations/0040_virtualmachine_serial_number.py diff --git a/docs/models/virtualization/virtualmachine.md b/docs/models/virtualization/virtualmachine.md index 769d49154..7a801ca65 100644 --- a/docs/models/virtualization/virtualmachine.md +++ b/docs/models/virtualization/virtualmachine.md @@ -51,3 +51,8 @@ The amount of running memory provisioned, in megabytes. ### Disk The amount of disk storage provisioned, in gigabytes. + +### Serial Number + +Optional serial number assigned to this VM. + diff --git a/netbox/templates/virtualization/virtualmachine.html b/netbox/templates/virtualization/virtualmachine.html index d2603ddd2..37cd11836 100644 --- a/netbox/templates/virtualization/virtualmachine.html +++ b/netbox/templates/virtualization/virtualmachine.html @@ -31,6 +31,10 @@ {% trans "Description" %} {{ object.description|placeholder }}
    {% trans "Serial Number" %}{{ object.serial|placeholder }}
    {% trans "Tenant" %} diff --git a/netbox/virtualization/api/serializers_/virtualmachines.py b/netbox/virtualization/api/serializers_/virtualmachines.py index 53146e44a..ea1021a25 100644 --- a/netbox/virtualization/api/serializers_/virtualmachines.py +++ b/netbox/virtualization/api/serializers_/virtualmachines.py @@ -49,9 +49,9 @@ class VirtualMachineSerializer(NetBoxModelSerializer): class Meta: model = VirtualMachine fields = [ - 'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform', - 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments', - 'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated', + 'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'serial', 'role', 'tenant', + 'platform', 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', + 'comments', 'config_template', 'local_context_data', 'tags', 'custom_fields', 'created', 'last_updated', 'interface_count', 'virtual_disk_count', ] brief_fields = ('id', 'url', 'display', 'name', 'description') @@ -62,9 +62,9 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer): class Meta(VirtualMachineSerializer.Meta): fields = [ - 'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'platform', - 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments', - 'config_template', 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created', + 'id', 'url', 'display', 'name', 'status', 'site', 'cluster', 'device', 'serial', 'role', 'tenant', + 'platform', 'primary_ip', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', + 'comments', 'config_template', 'local_context_data', 'tags', 'custom_fields', 'config_context', 'created', 'last_updated', 'interface_count', 'virtual_disk_count', ] diff --git a/netbox/virtualization/filtersets.py b/netbox/virtualization/filtersets.py index 55fadd1af..ec0831f9f 100644 --- a/netbox/virtualization/filtersets.py +++ b/netbox/virtualization/filtersets.py @@ -240,7 +240,10 @@ class VirtualMachineFilterSet( class Meta: model = VirtualMachine - fields = ('id', 'cluster', 'vcpus', 'memory', 'disk', 'description', 'interface_count', 'virtual_disk_count') + fields = ( + 'id', 'cluster', 'vcpus', 'memory', 'disk', 'description', 'interface_count', 'virtual_disk_count', + 'serial' + ) def search(self, queryset, name, value): if not value.strip(): @@ -250,7 +253,8 @@ class VirtualMachineFilterSet( Q(description__icontains=value) | Q(comments__icontains=value) | Q(primary_ip4__address__startswith=value) | - Q(primary_ip6__address__startswith=value) + Q(primary_ip6__address__startswith=value) | + Q(serial__icontains=value) ) def _has_primary_ip(self, queryset, name, value): diff --git a/netbox/virtualization/forms/bulk_import.py b/netbox/virtualization/forms/bulk_import.py index 5d44ddceb..17efc567a 100644 --- a/netbox/virtualization/forms/bulk_import.py +++ b/netbox/virtualization/forms/bulk_import.py @@ -137,7 +137,7 @@ class VirtualMachineImportForm(NetBoxModelImportForm): model = VirtualMachine fields = ( 'name', 'status', 'role', 'site', 'cluster', 'device', 'tenant', 'platform', 'vcpus', 'memory', 'disk', - 'description', 'config_template', 'comments', 'tags', + 'description', 'serial', 'config_template', 'comments', 'tags', ) diff --git a/netbox/virtualization/forms/filtersets.py b/netbox/virtualization/forms/filtersets.py index 1cb652a1b..7c040d948 100644 --- a/netbox/virtualization/forms/filtersets.py +++ b/netbox/virtualization/forms/filtersets.py @@ -100,7 +100,7 @@ class VirtualMachineFilterForm( FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')), FieldSet( 'status', 'role_id', 'platform_id', 'mac_address', 'has_primary_ip', 'config_template_id', - 'local_context_data', name=_('Attributes') + 'local_context_data', 'serial', name=_('Attributes') ), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('contact', 'contact_role', 'contact_group', name=_('Contacts')), @@ -178,6 +178,10 @@ class VirtualMachineFilterForm( choices=BOOLEAN_WITH_BLANK_CHOICES ) ) + serial = forms.CharField( + required=False, + label=_('Serial number') + ) config_template_id = DynamicModelMultipleChoiceField( queryset=ConfigTemplate.objects.all(), required=False, diff --git a/netbox/virtualization/forms/model_forms.py b/netbox/virtualization/forms/model_forms.py index bfdfc9ada..2c60cb46f 100644 --- a/netbox/virtualization/forms/model_forms.py +++ b/netbox/virtualization/forms/model_forms.py @@ -217,7 +217,7 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm): comments = CommentField() fieldsets = ( - FieldSet('name', 'role', 'status', 'description', 'tags', name=_('Virtual Machine')), + FieldSet('name', 'role', 'status', 'description', 'serial', 'tags', name=_('Virtual Machine')), FieldSet('site', 'cluster', 'device', name=_('Site/Cluster')), FieldSet('tenant_group', 'tenant', name=_('Tenancy')), FieldSet('platform', 'primary_ip4', 'primary_ip6', 'config_template', name=_('Management')), @@ -229,8 +229,8 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm): model = VirtualMachine fields = [ 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant_group', 'tenant', 'platform', 'primary_ip4', - 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'comments', 'tags', 'local_context_data', - 'config_template', + 'primary_ip6', 'vcpus', 'memory', 'disk', 'description', 'serial', 'comments', 'tags', + 'local_context_data', 'config_template', ] def __init__(self, *args, **kwargs): diff --git a/netbox/virtualization/migrations/0040_virtualmachine_serial_number.py b/netbox/virtualization/migrations/0040_virtualmachine_serial_number.py new file mode 100644 index 000000000..5ca72d66d --- /dev/null +++ b/netbox/virtualization/migrations/0040_virtualmachine_serial_number.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.6 on 2024-06-04 17:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('virtualization', '0039_convert_disk_size'), + ] + + operations = [ + migrations.AddField( + model_name='virtualmachine', + name='serial', + field=models.CharField(blank=True, max_length=50), + ), + ] diff --git a/netbox/virtualization/models/virtualmachines.py b/netbox/virtualization/models/virtualmachines.py index fc446922b..0c3fc5f4a 100644 --- a/netbox/virtualization/models/virtualmachines.py +++ b/netbox/virtualization/models/virtualmachines.py @@ -127,6 +127,11 @@ class VirtualMachine(ContactsMixin, ImageAttachmentsMixin, RenderConfigMixin, Co null=True, verbose_name=_('disk (MB)') ) + serial = models.CharField( + verbose_name=_('serial number'), + blank=True, + max_length=50 + ) # Counter fields interface_count = CounterCacheField( diff --git a/netbox/virtualization/search.py b/netbox/virtualization/search.py index c72b3345b..cdaf0c074 100644 --- a/netbox/virtualization/search.py +++ b/netbox/virtualization/search.py @@ -39,11 +39,12 @@ class ClusterTypeIndex(SearchIndex): class VirtualMachineIndex(SearchIndex): model = models.VirtualMachine fields = ( + ('serial', 60), ('name', 100), ('description', 500), ('comments', 5000), ) - display_attrs = ('site', 'cluster', 'device', 'tenant', 'platform', 'status', 'role', 'description') + display_attrs = ('site', 'cluster', 'device', 'tenant', 'platform', 'status', 'serial', 'role', 'description') @register_search diff --git a/netbox/virtualization/tables/virtualmachines.py b/netbox/virtualization/tables/virtualmachines.py index f37a1bebc..50be18ac5 100644 --- a/netbox/virtualization/tables/virtualmachines.py +++ b/netbox/virtualization/tables/virtualmachines.py @@ -116,7 +116,7 @@ class VirtualMachineTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable) fields = ( 'pk', 'id', 'name', 'status', 'site', 'cluster', 'device', 'role', 'tenant', 'tenant_group', 'vcpus', 'memory', 'disk', 'primary_ip4', 'primary_ip6', 'primary_ip', 'description', 'comments', 'config_template', - 'contacts', 'tags', 'created', 'last_updated', + 'serial', 'contacts', 'tags', 'created', 'last_updated', ) default_columns = ( 'pk', 'name', 'status', 'site', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip', diff --git a/netbox/virtualization/tests/test_filtersets.py b/netbox/virtualization/tests/test_filtersets.py index ff55aba10..d2e6cc05f 100644 --- a/netbox/virtualization/tests/test_filtersets.py +++ b/netbox/virtualization/tests/test_filtersets.py @@ -327,7 +327,8 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests): memory=1, disk=1, description='foobar1', - local_context_data={"foo": 123} + local_context_data={"foo": 123}, + serial='111-aaa' ), VirtualMachine( name='Virtual Machine 2', @@ -341,7 +342,8 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests): vcpus=2, memory=2, disk=2, - description='foobar2' + description='foobar2', + serial='222-bbb' ), VirtualMachine( name='Virtual Machine 3', @@ -518,6 +520,10 @@ class VirtualMachineTestCase(TestCase, ChangeLoggedFilterSetTests): params = {'primary_ip6_id': [addresses[2].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) + def test_serial_number(self): + params = {'serial': ['111-aaa', '222-bbb']} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests): queryset = VMInterface.objects.all() diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py index ed6bef1e4..0daa55a5c 100644 --- a/netbox/virtualization/tests/test_views.py +++ b/netbox/virtualization/tests/test_views.py @@ -234,6 +234,7 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase): 'vcpus': 4, 'memory': 32768, 'disk': 4000, + 'serial': 'aaa-111', 'comments': 'Some comments', 'tags': [t.pk for t in tags], 'local_context_data': None,