diff --git a/.github/images/netbox_triage_bug.png b/.github/images/netbox_triage_bug.png
new file mode 100644
index 000000000..71dbb1842
Binary files /dev/null and b/.github/images/netbox_triage_bug.png differ
diff --git a/.github/images/netbox_triage_feature.png b/.github/images/netbox_triage_feature.png
new file mode 100644
index 000000000..d1bb2a371
Binary files /dev/null and b/.github/images/netbox_triage_feature.png differ
diff --git a/.github/images/netbox_triage_initial.png b/.github/images/netbox_triage_initial.png
new file mode 100644
index 000000000..b9f22650e
Binary files /dev/null and b/.github/images/netbox_triage_initial.png differ
diff --git a/.github/stale.yml b/.github/stale.yml
index 43401de8a..fdfb1d590 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -4,19 +4,19 @@
only: issues
# Number of days of inactivity before an issue becomes stale
-daysUntilStale: 14
+daysUntilStale: 45
# Number of days of inactivity before a stale issue is closed
-daysUntilClose: 7
+daysUntilClose: 15
# Issues with these labels will never be considered stale
exemptLabels:
- "status: accepted"
- - "status: gathering feedback"
- "status: blocked"
+ - "status: needs milestone"
# Label to use when marking an issue as stale
-staleLabel: wontfix
+staleLabel: "pending closure"
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cceea27b6..4f448f5ee 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -99,6 +99,10 @@ help prevent wasting time on something that might we might not be able to
implement. When suggesting a new feature, also make sure it won't conflict with
any work that's already in progress.
+* Once you've opened or identified an issue you'd like to work on, ask that it
+be assigned to you so that others are aware it's being worked on. A maintainer
+will then mark the issue as "accepted."
+
* Any pull request which does _not_ relate to an accepted issue will be closed.
* All major new functionality must include relevant tests where applicable.
@@ -132,18 +136,17 @@ accumulating a large backlog of work.
The core maintainers group has chosen to make use of GitHub's [Stale bot](https://github.com/apps/stale)
to aid in issue management.
-* Issues will be marked as stale after 14 days of no activity.
-* Then after 7 more days of inactivity, the issue will be closed.
+* Issues will be marked as stale after 45 days of no activity.
+* Then after 15 more days of inactivity, the issue will be closed.
* Any issue bearing one of the following labels will be exempt from all Stale
bot actions:
* `status: accepted`
- * `status: gathering feedback`
* `status: blocked`
+ * `status: needs milestone`
-It is natural that some new issues get more attention than others. Often this
-is a metric of an issues's overall value to the project. In other cases in
-which issues merely get lost in the shuffle, notifications from Stale bot can
-bring renewed attention to potentially meaningful issues.
+It is natural that some new issues get more attention than others. Stale bot
+helps bring renewed attention to potentially valuable issues that may have been
+overlooked.
## Maintainer Guidance
diff --git a/docs/api/overview.md b/docs/api/overview.md
index 8eefae027..90372aa2a 100644
--- a/docs/api/overview.md
+++ b/docs/api/overview.md
@@ -279,6 +279,10 @@ http://localhost:8000/api/ipam/prefixes/ | jq ".actions.POST.status.choices"
For most fields, when a filter is passed multiple times, objects matching _any_ of the provided values will be returned. For example, `GET /api/dcim/sites/?name=Foo&name=Bar` will return all sites named "Foo" _or_ "Bar". The exception to this rule is ManyToManyFields which may have multiple values assigned. Tags are the most common example of a ManyToManyField. For example, `GET /api/dcim/sites/?tag=foo&tag=bar` will return only sites tagged with both "foo" _and_ "bar".
+### Excluding Config Contexts
+
+The rendered config context for devices and VMs is included by default in all API results (list and detail views). Users with large amounts of context data will most likely observe a performance drop when returning multiple objects, particularly with page sizes in the high hundreds or more. To combat this, in cases where the rendered config context is not needed, the query parameter `?exclude=config_context` may be appended to the request URL to exclude the config context data from the API response.
+
### Custom Fields
To filter on a custom field, prepend `cf_` to the field name. For example, the following query will return only sites where a custom field named `foo` is equal to 123:
diff --git a/docs/development/release-checklist.md b/docs/development/release-checklist.md
index d924d2c0b..8fe86af80 100644
--- a/docs/development/release-checklist.md
+++ b/docs/development/release-checklist.md
@@ -41,7 +41,14 @@ Create a file at `/docs/release-notes/X.Y.md` to establish the release notes for
### Manually Perform a New Install
-Create a new installation of NetBox by following [the current documentation](http://netbox.readthedocs.io/en/latest/). This should be a manual process, so that issues with the documentation can be identified and corrected.
+Install `mkdocs` in your local environment, then start the documentation server:
+
+```no-highlight
+$ pip install -r docs/requirements.txt
+$ mkdocs serve
+```
+
+Follow these instructions to perform a new installation of NetBox. This process must _not_ be automated: The goal of this step is to catch any errors or omissions in the documentation, and ensure that it is kept up-to-date for each release. Make any necessary changes to the documentation before proceeding with the release.
### Close the Release Milestone
diff --git a/docs/installation/3-netbox.md b/docs/installation/3-netbox.md
index c583d08fe..895e4237f 100644
--- a/docs/installation/3-netbox.md
+++ b/docs/installation/3-netbox.md
@@ -74,12 +74,18 @@ Checking connectivity... done.
Create a system user account named `netbox`. We'll configure the WSGI and HTTP services to run under this account. We'll also assign this user ownership of the media directory. This ensures that NetBox will be able to save local files.
-!!! note
- CentOS users may need to create the `netbox` group first.
+#### Ubuntu
+
+```
+# adduser --system --group netbox
+# chown --recursive netbox /opt/netbox/netbox/media/
+```
+
+#### CentOS
```
# groupadd --system netbox
-# adduser --system --gid netbox netbox
+# adduser --system -g netbox netbox
# chown --recursive netbox /opt/netbox/netbox/media/
```
diff --git a/docs/installation/upgrading.md b/docs/installation/upgrading.md
index c34fef954..807b9b1e6 100644
--- a/docs/installation/upgrading.md
+++ b/docs/installation/upgrading.md
@@ -30,6 +30,12 @@ Copy the 'configuration.py' you created when first installing to the new version
# cp netbox-X.Y.Z/netbox/netbox/configuration.py netbox/netbox/netbox/configuration.py
```
+Copy your local requirements file if used:
+
+```no-highlight
+# cp netbox-X.Y.Z/local_requirements.txt netbox/local_requirements.txt
+```
+
Also copy the LDAP configuration if using LDAP:
```no-highlight
diff --git a/docs/models/dcim/powerfeed.md b/docs/models/dcim/powerfeed.md
index ab8621e14..690e755d7 100644
--- a/docs/models/dcim/powerfeed.md
+++ b/docs/models/dcim/powerfeed.md
@@ -2,7 +2,7 @@
A power feed identifies the power outlet/drop that goes to a rack and is terminated to a power panel. Power feeds have a supply type (AC/DC), voltage, amperage, and phase type (single/three).
-Power feeds are optionally assigned to a rack. In addition, a power port – and only one – can connect to a power feed; in the context of a PDU, the power feed is analogous to the power outlet that a PDU's power port/inlet connects to.
+Power feeds are optionally assigned to a rack. In addition, a power port may be connected to a power feed. In the context of a PDU, the power feed is analogous to the power outlet that a PDU's power port/inlet connects to.
!!! info
The power usage of a rack is calculated when a power feed (or multiple) is assigned to that rack and connected to a power port.
diff --git a/docs/models/extras/imageattachment.md b/docs/models/extras/imageattachment.md
new file mode 100644
index 000000000..da15462ab
--- /dev/null
+++ b/docs/models/extras/imageattachment.md
@@ -0,0 +1,3 @@
+# Image Attachments
+
+Certain objects in NetBox support the attachment of uploaded images. These will be saved to the NetBox server and made available whenever the object is viewed.
diff --git a/docs/plugins/development.md b/docs/plugins/development.md
index ad7eef310..b704ad7fc 100644
--- a/docs/plugins/development.md
+++ b/docs/plugins/development.md
@@ -110,6 +110,8 @@ NetBox looks for the `config` variable within a plugin's `__init__.py` to load i
| `template_extensions` | The dotted path to the list of template extension classes (default: `template_content.template_extensions`) |
| `menu_items` | The dotted path to the list of menu items provided by the plugin (default: `navigation.menu_items`) |
+All required settings must be configured by the user. If a configuration parameter is listed in both `required_settings` and `default_settings`, the default setting will be ignored.
+
### Install the Plugin for Development
To ease development, it is recommended to go ahead and install the plugin at this point using setuptools' `develop` mode. This will create symbolic links within your Python environment to the plugin development directory. Call `setup.py` from the plugin's root directory with the `develop` argument (instead of `install`):
diff --git a/docs/release-notes/version-2.8.md b/docs/release-notes/version-2.8.md
index d3f566e59..af758f928 100644
--- a/docs/release-notes/version-2.8.md
+++ b/docs/release-notes/version-2.8.md
@@ -1,5 +1,28 @@
# NetBox v2.8
+## v2.8.9 (2020-08-04)
+
+### Enhancements
+
+* [#4898](https://github.com/netbox-community/netbox/issues/4898) - Add MAC address search field to interfaces list
+* [#4899](https://github.com/netbox-community/netbox/issues/4899) - Add MAC address column to interfaces table
+
+### Bug Fixes
+
+* [#4455](https://github.com/netbox-community/netbox/issues/4455) - Fix ordering of prefixes beneath aggregate when available space is hidden
+* [#4875](https://github.com/netbox-community/netbox/issues/4875) - Fix documentation for image attachments
+* [#4876](https://github.com/netbox-community/netbox/issues/4876) - Fix labels for sites in staging or decommissioning status
+* [#4880](https://github.com/netbox-community/netbox/issues/4880) - Fix removal of tagged VLANs if not assigned in bulk interface editing
+* [#4887](https://github.com/netbox-community/netbox/issues/4887) - Don't disable NAPALM tabs when device has no primary IP
+* [#4894](https://github.com/netbox-community/netbox/issues/4894) - Fix display of device/VM counts on platforms list
+* [#4895](https://github.com/netbox-community/netbox/issues/4895) - Force UTF-8 encoding when embedding model documentation
+* [#4910](https://github.com/netbox-community/netbox/issues/4910) - Unpin redis dependency to fix exception in RQ worker
+* [#4926](https://github.com/netbox-community/netbox/issues/4926) - Fix ordering of VM interfaces in REST API endpoint
+* [#4927](https://github.com/netbox-community/netbox/issues/4927) - Fix validation error when updating an existing secret
+* [#4929](https://github.com/netbox-community/netbox/issues/4929) - Correct log message when creating a new object
+
+---
+
## v2.8.8 (2020-07-21)
### Enhancements
diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py
index 9d9d18baf..4de81ac54 100644
--- a/netbox/dcim/forms.py
+++ b/netbox/dcim/forms.py
@@ -2671,6 +2671,10 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
+ mac_address = forms.CharField(
+ required=False,
+ label='MAC address'
+ )
tag = TagFilterField(model)
diff --git a/netbox/dcim/models/__init__.py b/netbox/dcim/models/__init__.py
index 993de734c..ef5b07aca 100644
--- a/netbox/dcim/models/__init__.py
+++ b/netbox/dcim/models/__init__.py
@@ -254,8 +254,10 @@ class Site(ChangeLoggedModel, CustomFieldModel):
]
STATUS_CLASS_MAP = {
- SiteStatusChoices.STATUS_ACTIVE: 'success',
SiteStatusChoices.STATUS_PLANNED: 'info',
+ SiteStatusChoices.STATUS_STAGING: 'primary',
+ SiteStatusChoices.STATUS_ACTIVE: 'success',
+ SiteStatusChoices.STATUS_DECOMMISSIONING: 'warning',
SiteStatusChoices.STATUS_RETIRED: 'danger',
}
diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py
index e7fd6ae11..93ef724e0 100644
--- a/netbox/dcim/tables.py
+++ b/netbox/dcim/tables.py
@@ -94,6 +94,14 @@ MANUFACTURER_ACTIONS = """
{% endif %}
"""
+DEVICEROLE_DEVICE_COUNT = """
+{{ value|default:0 }}
+"""
+
+DEVICEROLE_VM_COUNT = """
+{{ value|default:0 }}
+"""
+
DEVICEROLE_ACTIONS = """
@@ -103,12 +111,12 @@ DEVICEROLE_ACTIONS = """
{% endif %}
"""
-DEVICE_COUNT = """
-{{ value|default:0 }}
+PLATFORM_DEVICE_COUNT = """
+{{ value|default:0 }}
"""
-VM_COUNT = """
-{{ value|default:0 }}
+PLATFORM_VM_COUNT = """
+{{ value|default:0 }}
"""
PLATFORM_ACTIONS = """
@@ -697,11 +705,11 @@ class DeviceBayTemplateTable(BaseTable):
class DeviceRoleTable(BaseTable):
pk = ToggleColumn()
device_count = tables.TemplateColumn(
- template_code=DEVICE_COUNT,
+ template_code=DEVICEROLE_DEVICE_COUNT,
verbose_name='Devices'
)
vm_count = tables.TemplateColumn(
- template_code=VM_COUNT,
+ template_code=DEVICEROLE_VM_COUNT,
verbose_name='VMs'
)
color = tables.TemplateColumn(
@@ -728,11 +736,11 @@ class DeviceRoleTable(BaseTable):
class PlatformTable(BaseTable):
pk = ToggleColumn()
device_count = tables.TemplateColumn(
- template_code=DEVICE_COUNT,
+ template_code=PLATFORM_DEVICE_COUNT,
verbose_name='Devices'
)
vm_count = tables.TemplateColumn(
- template_code=VM_COUNT,
+ template_code=PLATFORM_VM_COUNT,
verbose_name='VMs'
)
actions = tables.TemplateColumn(
@@ -950,8 +958,8 @@ class InterfaceDetailTable(DeviceComponentDetailTable):
class Meta(InterfaceTable.Meta):
order_by = ('parent', 'name')
- fields = ('pk', 'parent', 'name', 'enabled', 'type', 'description', 'cable')
- sequence = ('pk', 'parent', 'name', 'enabled', 'type', 'description', 'cable')
+ fields = ('pk', 'parent', 'name', 'enabled', 'type', 'mac_address', 'description', 'cable')
+ default_columns = ('pk', 'parent', 'name', 'enabled', 'type', 'description', 'cable')
class FrontPortTable(BaseTable):
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
index bb5182a83..e193813d2 100644
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -1060,8 +1060,8 @@ class DeviceRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
class PlatformListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_platform'
queryset = Platform.objects.annotate(
- device_count=get_subquery(Device, 'device_role'),
- vm_count=get_subquery(VirtualMachine, 'role')
+ device_count=get_subquery(Device, 'platform'),
+ vm_count=get_subquery(VirtualMachine, 'platform')
)
table = tables.PlatformTable
diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py
index fa81de77b..db8a75852 100644
--- a/netbox/ipam/views.py
+++ b/netbox/ipam/views.py
@@ -327,6 +327,8 @@ class AggregateView(PermissionRequiredMixin, View):
prefix__net_contained_or_equal=str(aggregate.prefix)
).prefetch_related(
'site', 'role'
+ ).order_by(
+ 'prefix'
).annotate_depth(
limit=0
)
diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py
index ab6bba152..1c7a8acc8 100644
--- a/netbox/netbox/settings.py
+++ b/netbox/netbox/settings.py
@@ -16,7 +16,7 @@ from django.core.validators import URLValidator
# Environment setup
#
-VERSION = '2.8.8'
+VERSION = '2.8.9'
# Hostname
HOSTNAME = platform.node()
diff --git a/netbox/project-static/jquery/jquery-3.4.1.min.js b/netbox/project-static/jquery/jquery-3.4.1.min.js
deleted file mode 100644
index a1c07fd80..000000000
--- a/netbox/project-static/jquery/jquery-3.4.1.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */
-!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"