diff --git a/README.md b/README.md index 9d07699..17434c8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Netbox Topology Views Plugin +![Version](https://img.shields.io/pypi/v/netbox-topology-views) ![Downloads](https://img.shields.io/pypi/dm/netbox-topology-views) + Create topology views/maps from your devices in netbox. The connections are based on the cables you created in netbox. Support to filter on name, site, tag and device role. diff --git a/netbox_topology_views/__init__.py b/netbox_topology_views/__init__.py index 76c83a5..f74d547 100644 --- a/netbox_topology_views/__init__.py +++ b/netbox_topology_views/__init__.py @@ -4,7 +4,7 @@ class TopologyViewsConfig(PluginConfig): name = 'netbox_topology_views' verbose_name = 'Topology views' description = 'An plugin to render toplogoy maps' - version = '0.4.10' + version = '0.5.0' author = 'Mattijs Vanhaverbeke' author_email = 'author@example.com' base_url = 'topology-views' diff --git a/netbox_topology_views/api/__init__.py b/netbox_topology_views/api/__init__.py new file mode 100644 index 0000000..2bf6ee2 --- /dev/null +++ b/netbox_topology_views/api/__init__.py @@ -0,0 +1 @@ +"""REST API""" \ No newline at end of file diff --git a/netbox_topology_views/api/urls.py b/netbox_topology_views/api/urls.py index 831a77b..239e6cb 100644 --- a/netbox_topology_views/api/urls.py +++ b/netbox_topology_views/api/urls.py @@ -1,11 +1,13 @@ -from rest_framework import routers +from netbox.api import OrderedDefaultRouter +from . import views -from .views import PreSelectDeviceRolesViewSet, SearchViewSet, SaveCoordsViewSet, PreSelectTagsViewSet +router = OrderedDefaultRouter() +router.APIRootView = views.TopologyViewsRootView -router = routers.DefaultRouter() -router.register('preselectdeviceroles', PreSelectDeviceRolesViewSet) -router.register('preselecttags', PreSelectTagsViewSet) -router.register('search', SearchViewSet, basename='search') -router.register('save-coords', SaveCoordsViewSet, basename='save_coords') + +router.register('preselectdeviceroles', views.PreSelectDeviceRolesViewSet) +router.register('preselecttags', views.PreSelectTagsViewSet) +router.register('search', views.SearchViewSet, basename='search') +router.register('save-coords', views.SaveCoordsViewSet, basename='save_coords') urlpatterns = router.urls \ No newline at end of file diff --git a/netbox_topology_views/api/views.py b/netbox_topology_views/api/views.py index 5d545d2..9e673b5 100644 --- a/netbox_topology_views/api/views.py +++ b/netbox_topology_views/api/views.py @@ -2,15 +2,15 @@ from rest_framework.viewsets import ModelViewSet, ViewSet, ReadOnlyModelViewSet, from rest_framework.decorators import action from rest_framework.response import Response from django.contrib.contenttypes.models import ContentType +from rest_framework.routers import APIRootView from .serializers import PreDeviceRoleSerializer, TopologyDummySerializer, PreTagSerializer from django.conf import settings -from utilities.api import IsAuthenticatedOrLoginNotRequired - from dcim.models import DeviceRole, Device, Cable from circuits.models import Circuit -from extras.models import Tag, CustomField, CustomFieldValue +from extras.models import Tag + ignore_cable_type_raw = settings.PLUGINS_CONFIG["netbox_topology_views"]["ignore_cable_type"] ignore_cable_type = ignore_cable_type_raw.split(",") @@ -21,6 +21,9 @@ preselected_device_roles = preselected_device_roles_raw.split(",") preselected_tags_raw = settings.PLUGINS_CONFIG["netbox_topology_views"]["preselected_tags"] preselected_tags = preselected_tags_raw.split(",") +class TopologyViewsRootView(APIRootView): + def get_view_name(self): + return 'TopologyViews' class PreSelectDeviceRolesViewSet(ReadOnlyModelViewSet): queryset = DeviceRole.objects.filter(name__in=preselected_device_roles) @@ -38,14 +41,6 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet): def save_coords(self, request): results = {} if settings.PLUGINS_CONFIG["netbox_topology_views"]["allow_coordinates_saving"]: - try: - cfCoords = CustomField.objects.get(name='coordinates') - except CustomField.DoesNotExist: - results["status"] = "coords custom field not created" - return Response(status=500) - - obj_type = ContentType.objects.get_for_model(Device) - device_id = None x_coord = None y_coord = None @@ -60,26 +55,26 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet): y_coord = request.data["y"] actual_device= Device.objects.get(id=device_id) - try: - cfvCoords = CustomFieldValue.objects.get(obj_type=obj_type, obj_id=actual_device.pk, field__name='coordinates') - cfvCoords.value = "%s;%s" % (x_coord,y_coord) - cfvCoords.save() - results["status"] = "coords set" - except CustomFieldValue.DoesNotExist: - cfvCoords = CustomFieldValue(field=cfCoords, obj_type=obj_type, obj_id=actual_device.id) - cfvCoords.value = "%s;%s" % (x_coord,y_coord) - cfvCoords.save() - results["status"] = "coords set for first time" - + + if "coordinates" in actual_device.custom_field_data: + actual_device.custom_field_data["coordinates"] = "%s;%s" % (x_coord,y_coord) + actual_device.save() + results["status"] = "saved coords" + else: + try: + actual_device.custom_field_data["coordinates"] = "%s;%s" % (x_coord,y_coord) + actual_device.save() + results["status"] = "saved coords" + except : + results["status"] = "coords custom field not created" + return Response(status=500) + return Response(results) else: results["status"] = "not allowed to save coords" return Response(results, status=500) class SearchViewSet(ReadOnlyModelViewSet): - #_ignore_model_permissions = True - #permission_classes = [IsAuthenticatedOrLoginNotRequired] - queryset = Device.objects.all() serializer_class = TopologyDummySerializer @@ -245,9 +240,9 @@ class SearchViewSet(ReadOnlyModelViewSet): if device.device_role.color != "": node["color.border"] = "#" + device.device_role.color - for device_custom_field in device.custom_field_values.all(): - if device_custom_field.field.name == "coordinates": - cords = device_custom_field.serialized_value.split(";") + if "coordinates" in device.custom_field_data: + if device.custom_field_data["coordinates"] != "": + cords = device.custom_field_data["coordinates"].split(";") node["x"] = int(cords[0]) node["y"] = int(cords[1]) node["physics"] = False @@ -259,3 +254,4 @@ class SearchViewSet(ReadOnlyModelViewSet): results["edges"] = edges return Response(results) + \ No newline at end of file diff --git a/netbox_topology_views/static/netbox_topology_views/js/app.js b/netbox_topology_views/static/netbox_topology_views/js/app.js index ea93b80..9fbae8d 100644 --- a/netbox_topology_views/static/netbox_topology_views/js/app.js +++ b/netbox_topology_views/static/netbox_topology_views/js/app.js @@ -1 +1 @@ -var graph=null,container=null;function iniPlotboxFull(){document.addEventListener("DOMContentLoaded",function(){container=document.getElementById("fullvisgraph"),startRender()},!1)}function startRender(){var e=location.search;$.ajax({type:"GET",url:"../../api/plugins/topology-views/search/search/"+e,contentType:"application/json; charset=utf-8",success:function(e,t,n){graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),$.each(e.nodes,function(e,t){nodes.add(t)}),$.each(e.edges,function(e,t){edges.add(t)}),graph.fit(),canvas=document.getElementById("fullvisgraph").getElementsByTagName("canvas")[0]}})}var graph=null,container=null,downloadButton=null,MIME_TYPE="image/png",canvas=null,csrftoken=null,nodes=new vis.DataSet,edges=new vis.DataSet,options={interaction:{hover:!0,hoverConnectedEdges:!0,multiselect:!1},nodes:{shape:"image",brokenImage:"../../static/netbox_topology_views/img/role-unknown.png",size:35,font:{multi:"md",face:"helvetica"}},edges:{length:100,width:2,font:{face:"helvetica"}},physics:{solver:"forceAtlas2Based"}},selected_regions=[];function iniPlotboxIndex(){document.addEventListener("DOMContentLoaded",function(){container=document.getElementById("visgraph"),csrftoken=jQuery("[name=csrfmiddlewaretoken]").val(),startLoadSearchBar(),handleButtonPress(),downloadButton=document.getElementById("btnDownloadImage"),btnFullView=document.getElementById("btnFullView")},!1)}function startLoadSearchBar(){$("#device-roles").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/dcim/device-roles/?brief=true",dataType:"json",type:"GET",data:function(e){return{q:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}}),$("#sites").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:function(e){var t="../../api/dcim/sites/?brief=true";if(0==selected_regions.length)return t;for(var n=0;nLoading data'),e.preventDefault();var t=$("#name").val(),n=$("#device-roles").val(),a=$("#sites").val(),s=$("#tags").val(),o=$("#regions").val();$.ajax({type:"GET",url:"../../api/plugins/topology-views/search/search/",data:{name:t,"devicerole[]":n,"sites[]":a,"tags[]":s,"regions[]":o},headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",dataType:"json",beforeSend:function(){new_url=this.url.split("../../api/plugins/topology-views/search/search/?"),new_url=new_url[1],new_url="../../plugins/topology-views/full?"+new_url,btnFullView.classList.remove("disabled"),btnFullView.href=new_url},success:function(e,t,n){$("#status").html('Drawing network'),graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),$.each(e.nodes,function(e,t){nodes.add(t)}),$.each(e.edges,function(e,t){edges.add(t)}),graph.fit(),canvas=document.getElementById("visgraph").getElementsByTagName("canvas")[0],graph.on("afterDrawing",function(){$("#status").html('Ready');var e=canvas.toDataURL(MIME_TYPE);downloadButton.href=e,downloadButton.download="topology"}),graph.on("dragEnd",function(e){dragged=this.getPositions(e.nodes),$.each(dragged,function(e,t){$("#coordstatus").html(""),$("#checkSaveCoordinates").is(":checked")&&$.ajax({url:"../../api/plugins/topology-views/save-coords/save_coords/",type:"PATCH",dataType:"json",headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",processData:!1,data:JSON.stringify({node_id:e,x:t.x,y:t.y}),error:function(e){$("#coordstatus").html('Failed to update coordinates')},success:function(e){$("#coordstatus").html('Updated coordinates')}})})})},error:function(e){$("#status").html('Something went wrong')}})})} \ No newline at end of file +var graph=null,container=null;function iniPlotboxFull(){document.addEventListener("DOMContentLoaded",function(){container=document.getElementById("fullvisgraph"),startRender()},!1)}function startRender(){var e=location.search;$.ajax({type:"GET",url:"../../api/plugins/topology-views/search/search/"+e,contentType:"application/json; charset=utf-8",success:function(e,t,n){graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),$.each(e.nodes,function(e,t){nodes.add(t)}),$.each(e.edges,function(e,t){edges.add(t)}),graph.fit(),canvas=document.getElementById("fullvisgraph").getElementsByTagName("canvas")[0]}})}graph=null,container=null;var downloadButton=null,MIME_TYPE="image/png",canvas=null,csrftoken=null,nodes=new vis.DataSet,edges=new vis.DataSet,options={interaction:{hover:!0,hoverConnectedEdges:!0,multiselect:!1},nodes:{shape:"image",brokenImage:"../../static/netbox_topology_views/img/role-unknown.png",size:35,font:{multi:"md",face:"helvetica"}},edges:{length:100,width:2,font:{face:"helvetica"}},physics:{solver:"forceAtlas2Based"}},selected_regions=[];function iniPlotboxIndex(){document.addEventListener("DOMContentLoaded",function(){container=document.getElementById("visgraph"),csrftoken=jQuery("[name=csrfmiddlewaretoken]").val(),startLoadSearchBar(),handleButtonPress(),downloadButton=document.getElementById("btnDownloadImage"),btnFullView=document.getElementById("btnFullView")},!1)}function startLoadSearchBar(){$("#device-roles").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:"../../api/dcim/device-roles/?brief=true",dataType:"json",type:"GET",data:function(e){return{q:e.term}},processResults:function(e){return{results:$.map(e.results,function(e){return{text:e.name,id:e.id}})}}}}),$("#sites").select2({allowClear:!0,placeholder:"---------",theme:"bootstrap",multiple:!0,ajax:{url:function(e){var t="../../api/dcim/sites/?brief=true";if(0==selected_regions.length)return t;for(var n=0;nLoading data'),e.preventDefault();var t=$("#name").val(),n=$("#device-roles").val(),a=$("#sites").val(),s=$("#tags").val(),o=$("#regions").val();$.ajax({type:"GET",url:"../../api/plugins/topology-views/search/search/",data:{name:t,"devicerole[]":n,"sites[]":a,"tags[]":s,"regions[]":o},headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",dataType:"json",beforeSend:function(){new_url=this.url.split("../../api/plugins/topology-views/search/search/?"),new_url=new_url[1],new_url="../../plugins/topology-views/full?"+new_url,btnFullView.classList.remove("disabled"),btnFullView.href=new_url},success:function(e,t,n){$("#status").html('Drawing network'),graph=null,nodes=new vis.DataSet,edges=new vis.DataSet,graph=new vis.Network(container,{nodes:nodes,edges:edges},options),$.each(e.nodes,function(e,t){nodes.add(t)}),$.each(e.edges,function(e,t){edges.add(t)}),graph.fit(),canvas=document.getElementById("visgraph").getElementsByTagName("canvas")[0],graph.on("afterDrawing",function(){$("#status").html('Ready');var e=canvas.toDataURL(MIME_TYPE);downloadButton.href=e,downloadButton.download="topology"}),graph.on("dragEnd",function(e){dragged=this.getPositions(e.nodes),$.each(dragged,function(e,t){$("#coordstatus").html(""),$("#checkSaveCoordinates").is(":checked")&&$.ajax({url:"../../api/plugins/topology-views/save-coords/save_coords/",type:"PATCH",dataType:"json",headers:{"X-CSRFToken":csrftoken},contentType:"application/json; charset=utf-8",processData:!1,data:JSON.stringify({node_id:e,x:t.x,y:t.y}),error:function(e){$("#coordstatus").html('Failed to update coordinates')},success:function(e){$("#coordstatus").html('Updated coordinates')}})})})},error:function(e){$("#status").html('Something went wrong')}})})} \ No newline at end of file diff --git a/netbox_topology_views/static/netbox_topology_views/js/vendor.js b/netbox_topology_views/static/netbox_topology_views/js/vendor.js index 4ee9489..642a7ef 100644 --- a/netbox_topology_views/static/netbox_topology_views/js/vendor.js +++ b/netbox_topology_views/static/netbox_topology_views/js/vendor.js @@ -1 +1 @@ -!function(g,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((g=g||self).vis=g.vis||{})}(this,function(g){var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function e(g,t,e){return g(e={path:t,exports:{},require:function(g,t){return Le(null==t&&e.path)}},e.exports),e.exports}function A(g){return g&&g.default||g}function Le(){throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs")}function C(g){return g&&g.Math==Math&&g}function a(g){try{return!!g()}catch(g){return!0}}function v(g,t){return{enumerable:!(1&g),configurable:!(2&g),writable:!(4&g),value:t}}function I(g){return x.call(g).slice(8,-1)}function n(g){if(null==g)throw TypeError("Can't call method on "+g);return g}function r(g){return O(n(g))}function h(g){return"object"==typeof g?null!==g:"function"==typeof g}function i(g,t){if(!h(g))return g;var e,A;if(t&&"function"==typeof(e=g.toString)&&!h(A=e.call(g)))return A;if("function"==typeof(e=g.valueOf)&&!h(A=e.call(g)))return A;if(!t&&"function"==typeof(e=g.toString)&&!h(A=e.call(g)))return A;throw TypeError("Can't convert object to primitive value")}function p(g,t){return T.call(g,t)}function o(g){return N?E.createElement(g):{}}function s(g,t){var e=_[z(g)];return e==B||e!=Z&&("function"==typeof t?a(t):!!t)}function d(g){if("function"!=typeof g)throw TypeError(String(g)+" is not a function");return g}function y(A,C,g){if(d(A),void 0===C)return A;switch(g){case 0:return function(){return A.call(C)};case 1:return function(g){return A.call(C,g)};case 2:return function(g,t){return A.call(C,g,t)};case 3:return function(g,t,e){return A.call(C,g,t,e)}}return function(){return A.apply(C,arguments)}}function l(g){if(!h(g))throw TypeError(String(g)+" is not an object");return g}function f(A){function g(g,t,e){if(this instanceof A){switch(arguments.length){case 0:return new A;case 1:return new A(g);case 2:return new A(g,t)}return new A(g,t,e)}return A.apply(this,arguments)}return g.prototype=A.prototype,g}function m(g,t){var e,A,C,I,i,o,n,r,s=g.target,a=g.global,h=g.stat,d=g.proto,l=a?b:h?b[s]:(b[s]||{}).prototype,c=a?Y:Y[s]||(Y[s]={}),u=c.prototype;for(C in t)e=!F(a?C:s+(h?".":"#")+C,g.forced)&&l&&p(l,C),i=c[C],e&&(o=g.noTargetGet?(r=V(l,C))&&r.value:l[C]),I=e&&o?o:t[C],e&&typeof i==typeof I||(n=g.bind&&e?y(I,b):g.wrap&&e?f(I):d&&"function"==typeof I?y(Function.call,I):I,(g.sham||I&&I.sham||i&&i.sham)&&j(n,"sham",!0),c[C]=n,d&&(p(Y,A=s+"Prototype")||j(Y,A,{}),Y[A][C]=I,g.real&&u&&!u[C]&&j(u,C,I)))}var b=C("object"==typeof globalThis&&globalThis)||C("object"==typeof window&&window)||C("object"==typeof self&&self)||C("object"==typeof t&&t)||Function("return this")(),c=!a(function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}),u={}.propertyIsEnumerable,w=Object.getOwnPropertyDescriptor,k={f:w&&!u.call({1:2},1)?function(g){var t=w(this,g);return!!t&&t.enumerable}:u},x={}.toString,D="".split,O=a(function(){return!Object("z").propertyIsEnumerable(0)})?function(g){return"String"==I(g)?D.call(g,""):Object(g)}:Object,T={}.hasOwnProperty,E=b.document,N=h(E)&&h(E.createElement),R=!c&&!a(function(){return 7!=Object.defineProperty(o("div"),"a",{get:function(){return 7}}).a}),M=Object.getOwnPropertyDescriptor,P={f:c?M:function(g,t){if(g=r(g),t=i(t,!0),R)try{return M(g,t)}catch(g){}if(p(g,t))return v(!k.f.call(g,t),g[t])}},S=/#|\.prototype\./,z=s.normalize=function(g){return String(g).replace(S,".").toLowerCase()},_=s.data={},Z=s.NATIVE="N",B=s.POLYFILL="P",F=s,Y={},G=Object.defineProperty,L={f:c?G:function(g,t,e){if(l(g),t=i(t,!0),l(e),R)try{return G(g,t,e)}catch(g){}if("get"in e||"set"in e)throw TypeError("Accessors not supported");return"value"in e&&(g[t]=e.value),g}},j=c?function(g,t,e){return L.f(g,t,v(1,e))}:function(g,t,e){return g[t]=e,g},V=P.f,W=[].slice,H={},U=Function.bind||function(t){var e=d(this),A=W.call(arguments,1),C=function(){var g=A.concat(W.call(arguments));return this instanceof C?function(g,t,e){if(!(t in H)){for(var A=[],C=0;CC;)p(A,e=t[C++])&&(~ag(I,e)||I.push(e));return I}function gg(g){return Object(n(g))}var tg=Q("Function").bind,eg=Function.prototype,Ag=function(g){var t=g.bind;return g===eg||g instanceof Function&&t===eg.bind?tg:t},Cg=Math.ceil,Ig=Math.floor,ig=Math.min,og=Math.max,ng=Math.min,rg={includes:q(!0),indexOf:q(!1)},sg={},ag=rg.indexOf,hg=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],dg=Object.keys||function(g){return $(g,hg)},lg={f:Object.getOwnPropertySymbols},cg=Object.assign,ug=Object.defineProperty,pg=!cg||a(function(){if(c&&1!==cg({b:1},cg(ug({},"a",{enumerable:!0,get:function(){ug(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return 1;var g={},t={},e=Symbol();return g[e]=7,"abcdefghijklmnopqrst".split("").forEach(function(g){t[g]=g}),7!=cg({},g)[e]||"abcdefghijklmnopqrst"!=dg(cg({},t)).join("")})?function(g,t){for(var e=gg(g),A=arguments.length,C=1,I=lg.f,i=k.f;Cs&&(l=s),d=Math.sqrt(l*l/(1+r*r)),t+=d=o<0?-d:d,e+=r*d,!0===h?g.lineTo(t,e):g.moveTo(t,e),s-=l,h=!h}var kg={circle:vg,dashedLine:wg,database:bg,diamond:function(g,t,e,A){g.beginPath(),g.lineTo(t,e+A),g.lineTo(t+A,e),g.lineTo(t,e-A),g.lineTo(t-A,e),g.closePath()},ellipse:mg,ellipse_vis:mg,hexagon:function(g,t,e,A){g.beginPath();var C=2*Math.PI/6;g.moveTo(t+A,e);for(var I=1;I<6;I++)g.lineTo(t+A*Math.cos(C*I),e+A*Math.sin(C*I));g.closePath()},roundRect:yg,square:function(g,t,e,A){g.beginPath(),g.rect(t-A,e-A,2*A,2*A),g.closePath()},star:function(g,t,e,A){g.beginPath(),e+=.1*(A*=.82);for(var C=0;C<10;C++){var I=C%2==0?1.3*A:.5*A;g.lineTo(t+I*Math.sin(2*C*Math.PI/10),e-I*Math.cos(2*C*Math.PI/10))}g.closePath()},triangle:function(g,t,e,A){g.beginPath(),e+=.275*(A*=1.15);var C=2*A,I=C/2,i=Math.sqrt(3)/6*C,o=Math.sqrt(C*C-I*I);g.moveTo(t,e-(o-i)),g.lineTo(t+I,e+i),g.lineTo(t-I,e+i),g.lineTo(t,e-(o-i)),g.closePath()},triangleDown:function(g,t,e,A){g.beginPath(),e-=.275*(A*=1.15);var C=2*A,I=C/2,i=Math.sqrt(3)/6*C,o=Math.sqrt(C*C-I*I);g.moveTo(t,e+(o-i)),g.lineTo(t+I,e-i),g.lineTo(t-I,e-i),g.lineTo(t,e+(o-i)),g.closePath()}},xg=e(function(g){function e(g){if(g)return function(g){for(var t in e.prototype)g[t]=e.prototype[t];return g}(g)}(g.exports=e).prototype.on=e.prototype.addEventListener=function(g,t){return this._callbacks=this._callbacks||{},(this._callbacks["$"+g]=this._callbacks["$"+g]||[]).push(t),this},e.prototype.once=function(g,t){function e(){this.off(g,e),t.apply(this,arguments)}return e.fn=t,this.on(g,e),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(g,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var e,A=this._callbacks["$"+g];if(!A)return this;if(1==arguments.length)return delete this._callbacks["$"+g],this;for(var C=0;Co;)void 0!==(e=C(A,t=I[o++]))&&Rg(i,t,e);return i}});var _g=Y.Object.getOwnPropertyDescriptors,Zg=P.f,Bg=a(function(){Zg(1)});m({target:"Object",stat:!0,forced:!c||Bg,sham:!c},{getOwnPropertyDescriptor:function(g,t){return Zg(r(g),t)}});function Fg(g){return"Symbol("+String(void 0===g?"":g)+")_"+(++qg+$g).toString(36)}function Yg(g){return gt[g]||(gt[g]=Fg(g))}function Gg(){}function Lg(g){return"