diff --git a/docs/README.md b/docs/README.md
index 4f46655..61a04aa 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -8,9 +8,11 @@ Additional features: Export documents to JSON. Import documents from JSON. ISODa
Screenshots
-----------
-![MongoDB PHP GUI](https://raw.githubusercontent.com/SamuelTS/MongoDB-PHP-GUI/master/docs/screenshots/new-mpg-database-query.png)
+![MongoDB PHP GUI - Visualize Database](https://raw.githubusercontent.com/SamuelTS/MongoDB-PHP-GUI/master/docs/screenshots/newest-mpg-database-visualize.png)
-![MongoDB PHP GUI](https://raw.githubusercontent.com/SamuelTS/MongoDB-PHP-GUI/master/docs/screenshots/new-mpg-collection-indexes.png)
+![MongoDB PHP GUI - Query Database](https://raw.githubusercontent.com/SamuelTS/MongoDB-PHP-GUI/master/docs/screenshots/newest-mpg-database-query.png)
+
+![MongoDB PHP GUI - Manage Indexes](https://raw.githubusercontent.com/SamuelTS/MongoDB-PHP-GUI/master/docs/screenshots/newest-mpg-collection-indexes.png)
Installation
------------
@@ -24,7 +26,7 @@ Thanks
------
❤️ Thanks to [Limber](https://github.com/nimbly/Limber), [Capsule](https://github.com/nimbly/Capsule), [Font Awesome](https://fontawesome.com/), [Bootstrap](https://getbootstrap.com/), [CodeMirror](https://github.com/codemirror/codemirror) and [JsonView](https://github.com/pgrabovets/json-view).
-Special thanks to [MongoDB PHP library](https://github.com/mongodb/mongo-php-library) and [SQL to MongoDB Query Converter](https://github.com/vincentrussell/sql-to-mongo-db-query-converter). ❤️
+Thanks also to [MongoDB PHP lib](https://github.com/mongodb/mongo-php-library), [vis.js](https://github.com/visjs) and [SQL to MongoDB Query Converter](https://github.com/vincentrussell/sql-to-mongo-db-query-converter). ❤️
Copyright
---------
diff --git a/docs/screenshots/new-mpg-collection-indexes.png b/docs/screenshots/new-mpg-collection-indexes.png
deleted file mode 100644
index d141ed4..0000000
Binary files a/docs/screenshots/new-mpg-collection-indexes.png and /dev/null differ
diff --git a/docs/screenshots/new-mpg-database-query.png b/docs/screenshots/new-mpg-database-query.png
deleted file mode 100644
index 8c7eb81..0000000
Binary files a/docs/screenshots/new-mpg-database-query.png and /dev/null differ
diff --git a/docs/screenshots/newest-mpg-collection-indexes.png b/docs/screenshots/newest-mpg-collection-indexes.png
new file mode 100644
index 0000000..0794ed8
Binary files /dev/null and b/docs/screenshots/newest-mpg-collection-indexes.png differ
diff --git a/docs/screenshots/newest-mpg-database-query.png b/docs/screenshots/newest-mpg-database-query.png
new file mode 100644
index 0000000..489760e
Binary files /dev/null and b/docs/screenshots/newest-mpg-database-query.png differ
diff --git a/docs/screenshots/newest-mpg-database-visualize.png b/docs/screenshots/newest-mpg-database-visualize.png
new file mode 100644
index 0000000..b794b7b
Binary files /dev/null and b/docs/screenshots/newest-mpg-database-visualize.png differ
diff --git a/index.php b/index.php
index 9b8a025..76e6ed6 100644
--- a/index.php
+++ b/index.php
@@ -18,7 +18,7 @@
*
* @var string
*/
-define('MPG_APP_VERSION', '1.0.5');
+define('MPG_APP_VERSION', '1.0.6');
/**
* Development mode?
diff --git a/routes.php b/routes.php
index 7d487e3..55dacd9 100644
--- a/routes.php
+++ b/routes.php
@@ -66,6 +66,16 @@
CollectionController::class . '@renderImportViewAction'
);
+$router->get(
+ MPG_SERVER_PATH . '/visualizeDatabase',
+ DatabaseController::class . '@renderVisualizeViewAction'
+);
+
+$router->get(
+ MPG_SERVER_PATH . '/ajaxDatabaseGetNetworkGraph',
+ DatabaseController::class . '@getNetworkGraphAction'
+);
+
$router->get(
MPG_SERVER_PATH . '/queryDatabase',
DatabaseController::class . '@renderQueryViewAction'
diff --git a/src/Controllers/DatabaseController.php b/src/Controllers/DatabaseController.php
index 2bd582e..c68acdc 100644
--- a/src/Controllers/DatabaseController.php
+++ b/src/Controllers/DatabaseController.php
@@ -31,6 +31,107 @@ public static function getDatabaseNames() : array {
}
+ public function renderVisualizeViewAction() : Response {
+
+ LoginController::ensureUserIsLogged();
+
+ return new Response(200, $this->renderView('database.visualize'));
+
+ }
+
+ public function getNetworkGraphAction() : Response {
+
+ $networkGraph = [
+ 'visData' => [
+ 'nodes' => [
+ [
+ 'id' => 1,
+ 'label' => 'MongoDB server',
+ 'shape' => 'image',
+ 'image' => MPG_BASE_URL . '/static/images/leaf-icon.svg',
+ 'size' => 32
+ ]
+ ],
+ 'edges' => []
+ ],
+ 'mapping' => [
+
+ 1 => [
+ 'databaseName' => null,
+ 'collectionName' => null
+ ]
+
+ ]
+ ];
+
+ $nodeCounter = 1;
+
+ try {
+
+ foreach (MongoDBHelper::getClient()->listDatabases() as $databaseInfo) {
+
+ $nodeCounter++;
+
+ $databaseNode = [
+ 'id' => $nodeCounter,
+ 'label' => 'DB: ' . $databaseInfo['name'],
+ 'shape' => 'image',
+ 'image' => MPG_BASE_URL . '/static/images/database-icon.svg',
+ 'size' => 24
+ ];
+
+ $database = MongoDBHelper::getClient()->selectDatabase(
+ $databaseInfo['name']
+ );
+
+ foreach ($database->listCollections() as $collectionInfo) {
+
+ $nodeCounter++;
+
+ $collectionNode = [
+ 'id' => $nodeCounter,
+ 'label' => 'Coll: ' . $collectionInfo['name'],
+ 'shape' => 'image',
+ 'image' => MPG_BASE_URL . '/static/images/document-icon.svg',
+ 'size' => 24
+ ];
+
+ array_push($networkGraph['visData']['nodes'], $collectionNode);
+
+ array_push($networkGraph['visData']['edges'], [
+ 'from' => $databaseNode['id'],
+ 'to' => $collectionNode['id']
+ ]);
+
+ $networkGraph['mapping'][ $collectionNode['id'] ] = [
+ 'databaseName' => $databaseInfo['name'],
+ 'collectionName' => $collectionInfo['name']
+ ];
+
+ }
+
+ array_push($networkGraph['visData']['nodes'], $databaseNode);
+
+ array_push($networkGraph['visData']['edges'], [
+ 'from' => 1, // MongoDB server
+ 'to' => $databaseNode['id']
+ ]);
+
+ $networkGraph['mapping'][ $databaseNode['id'] ] = [
+ 'databaseName' => $databaseInfo['name'],
+ 'collectionName' => null
+ ];
+
+ }
+
+ } catch (\Throwable $th) {
+ return new JsonResponse(500, ErrorNormalizer::normalize($th, __METHOD__));
+ }
+
+ return new JsonResponse(200, $networkGraph);
+
+ }
+
public function renderQueryViewAction() : Response {
LoginController::ensureUserIsLogged();
diff --git a/static/css/mpg.css b/static/css/mpg.css
index c6bae6b..20f3771 100644
--- a/static/css/mpg.css
+++ b/static/css/mpg.css
@@ -145,6 +145,12 @@
}
+.vis-network:focus {
+
+ outline: none;
+
+}
+
code {
font-size: 100%;
diff --git a/static/images/database-icon.svg b/static/images/database-icon.svg
new file mode 100644
index 0000000..969d273
--- /dev/null
+++ b/static/images/database-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/images/document-icon.svg b/static/images/document-icon.svg
new file mode 100644
index 0000000..dfc34d3
--- /dev/null
+++ b/static/images/document-icon.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/static/images/leaf-icon.svg b/static/images/leaf-icon.svg
new file mode 100644
index 0000000..db43e67
--- /dev/null
+++ b/static/images/leaf-icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/js/mpg.database.query.js b/static/js/mpg.database.query.js
index 1704c1a..6446eca 100644
--- a/static/js/mpg.database.query.js
+++ b/static/js/mpg.database.query.js
@@ -489,6 +489,8 @@ MPG.eventListeners.addCollections = function() {
document.querySelector('#mpg-output-code').innerHTML = '';
+ document.querySelector('#mpg-find-button').click();
+
},
JSON.stringify(requestBody)
);
diff --git a/static/js/mpg.database.visualize.js b/static/js/mpg.database.visualize.js
new file mode 100644
index 0000000..84c9fa3
--- /dev/null
+++ b/static/js/mpg.database.visualize.js
@@ -0,0 +1,146 @@
+
+/**
+ * MongoDB PHP GUI namespace.
+ *
+ * @type {object}
+ */
+var MPG = {};
+
+/**
+ * vis.Network options.
+ *
+ * @type {object}
+ */
+MPG.visNetworkOptions = {
+
+ width: '100%',
+ height: '400px',
+ nodes: {
+ color: {
+ background: "transparent",
+ border: "transparent"
+ }
+ },
+ edges: {
+ width: 1,
+ color: {
+ color: '#ddd',
+ highlight: '#0062cc'
+ },
+ }
+
+};
+
+/**
+ * Helpers sub-namespace.
+ *
+ * @type {object}
+ */
+MPG.helpers = {};
+
+/**
+ * Does an ajax request.
+ *
+ * @param {string} method
+ * @param {string} url
+ * @param {function} successCallback
+ * @param {?string} body
+ *
+ * @returns {void}
+ */
+MPG.helpers.doAjaxRequest = function(method, url, successCallback, body) {
+
+ var xhr = new XMLHttpRequest();
+
+ xhr.addEventListener('readystatechange', function() {
+
+ if ( this.readyState === 4 ) {
+ if ( this.status === 200 ) {
+ successCallback(this.responseText);
+ } else {
+ window.alert('Error: ' + JSON.parse(this.responseText).error.message);
+ }
+ }
+
+ });
+
+ xhr.open(method, url);
+ xhr.send(body);
+
+};
+
+/**
+ * Event listeners sub-namespace.
+ *
+ * @type {object}
+ */
+MPG.eventListeners = {};
+
+/**
+ * Adds an event listener on "Menu toggle" button.
+ *
+ * @returns {void}
+ */
+MPG.eventListeners.addMenuToggle = function() {
+
+ document.querySelector('#menu-toggle-button').addEventListener('click', function(_event) {
+ document.querySelector('.navbar').classList.toggle('menu-expanded');
+ });
+
+};
+
+/**
+ * Draws vis.Network.
+ */
+MPG.drawVisNetwork = function() {
+
+ MPG.helpers.doAjaxRequest(
+ 'GET',
+ MPG_BASE_URL + '/ajaxDatabaseGetNetworkGraph',
+ function(response) {
+
+ var visNetworkContainer = document.querySelector('#vis-network-container');
+ var networkGraph = JSON.parse(response);
+
+ var visNetwork = new vis.Network(
+ visNetworkContainer, networkGraph.visData, MPG.visNetworkOptions
+ );
+
+ visNetwork.on('select', function(nodeProperties) {
+
+ if ( nodeProperties.nodes.length === 0 ) {
+ return;
+ }
+
+ var selectedNodeId = nodeProperties.nodes[0];
+ var selectedNodeMapping = networkGraph.mapping[selectedNodeId];
+ var targetUrl = MPG_BASE_URL + '/queryDatabase#';
+
+ if ( selectedNodeMapping.databaseName !== null ) {
+
+ targetUrl += selectedNodeMapping.databaseName;
+
+ if ( selectedNodeMapping.collectionName !== null ) {
+ targetUrl += '/' + selectedNodeMapping.collectionName;
+ }
+
+ window.location.href = targetUrl;
+
+ }
+
+
+ });
+
+ },
+ null
+ );
+
+};
+
+// When document is ready:
+window.addEventListener('DOMContentLoaded', function(_event) {
+
+ MPG.eventListeners.addMenuToggle();
+ MPG.drawVisNetwork();
+
+});
diff --git a/static/js/vis-network.min.js b/static/js/vis-network.min.js
new file mode 100644
index 0000000..8f2b946
--- /dev/null
+++ b/static/js/vis-network.min.js
@@ -0,0 +1,51 @@
+/**
+ * vis-network
+ * https://visjs.github.io/vis-network/
+ *
+ * A dynamic, browser-based visualization library.
+ *
+ * @version 8.2.0
+ * @date 2020-08-13T21:45:20.240Z
+ *
+ * @copyright (c) 2011-2017 Almende B.V, http://almende.com
+ * @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs
+ *
+ * @license
+ * vis.js is dual licensed under both
+ *
+ * 1. The Apache 2.0 License
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * and
+ *
+ * 2. The MIT License
+ * http://opensource.org/licenses/MIT
+ *
+ * vis.js may be distributed under either license.
+ */
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).vis=t.vis||{})}(this,(function(t){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function i(t,e){return t(e={exports:{}},e.exports),e.exports}var o=function(t){return t&&t.Math==Math&&t},n=o("object"==typeof globalThis&&globalThis)||o("object"==typeof window&&window)||o("object"==typeof self&&self)||o("object"==typeof e&&e)||Function("return this")(),r=function(t){try{return!!t()}catch(t){return!0}},s=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),a={}.propertyIsEnumerable,h=Object.getOwnPropertyDescriptor,l={f:h&&!a.call({1:2},1)?function(t){var e=h(this,t);return!!e&&e.enumerable}:a},d=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},c={}.toString,u=function(t){return c.call(t).slice(8,-1)},f="".split,p=r((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==u(t)?f.call(t,""):Object(t)}:Object,v=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},g=function(t){return p(v(t))},y=function(t){return"object"==typeof t?null!==t:"function"==typeof t},m=function(t,e){if(!y(t))return t;var i,o;if(e&&"function"==typeof(i=t.toString)&&!y(o=i.call(t)))return o;if("function"==typeof(i=t.valueOf)&&!y(o=i.call(t)))return o;if(!e&&"function"==typeof(i=t.toString)&&!y(o=i.call(t)))return o;throw TypeError("Can't convert object to primitive value")},b={}.hasOwnProperty,w=function(t,e){return b.call(t,e)},k=n.document,_=y(k)&&y(k.createElement),x=function(t){return _?k.createElement(t):{}},O=!s&&!r((function(){return 7!=Object.defineProperty(x("div"),"a",{get:function(){return 7}}).a})),E=Object.getOwnPropertyDescriptor,C={f:s?E:function(t,e){if(t=g(t),e=m(e,!0),O)try{return E(t,e)}catch(t){}if(w(t,e))return d(!l.f.call(t,e),t[e])}},S=/#|\.prototype\./,T=function(t,e){var i=M[D(t)];return i==I||i!=P&&("function"==typeof e?r(e):!!e)},D=T.normalize=function(t){return String(t).replace(S,".").toLowerCase()},M=T.data={},P=T.NATIVE="N",I=T.POLYFILL="P",B=T,z={},F=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t},N=function(t,e,i){if(F(t),void 0===e)return t;switch(i){case 0:return function(){return t.call(e)};case 1:return function(i){return t.call(e,i)};case 2:return function(i,o){return t.call(e,i,o)};case 3:return function(i,o,n){return t.call(e,i,o,n)}}return function(){return t.apply(e,arguments)}},A=function(t){if(!y(t))throw TypeError(String(t)+" is not an object");return t},j=Object.defineProperty,R={f:s?j:function(t,e,i){if(A(t),e=m(e,!0),A(i),O)try{return j(t,e,i)}catch(t){}if("get"in i||"set"in i)throw TypeError("Accessors not supported");return"value"in i&&(t[e]=i.value),t}},L=s?function(t,e,i){return R.f(t,e,d(1,i))}:function(t,e,i){return t[e]=i,t},H=C.f,W=function(t){var e=function(e,i,o){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,i)}return new t(e,i,o)}return t.apply(this,arguments)};return e.prototype=t.prototype,e},q=function(t,e){var i,o,r,s,a,h,l,d,c=t.target,u=t.global,f=t.stat,p=t.proto,v=u?n:f?n[c]:(n[c]||{}).prototype,g=u?z:z[c]||(z[c]={}),y=g.prototype;for(r in e)i=!B(u?r:c+(f?".":"#")+r,t.forced)&&v&&w(v,r),a=g[r],i&&(h=t.noTargetGet?(d=H(v,r))&&d.value:v[r]),s=i&&h?h:e[r],i&&typeof a==typeof s||(l=t.bind&&i?N(s,n):t.wrap&&i?W(s):p&&"function"==typeof s?N(Function.call,s):s,(t.sham||s&&s.sham||a&&a.sham)&&L(l,"sham",!0),g[r]=l,p&&(w(z,o=c+"Prototype")||L(z,o,{}),z[o][r]=s,t.real&&y&&!y[r]&&L(y,r,s)))},V=[].slice,U={},Y=function(t,e,i){if(!(e in U)){for(var o=[],n=0;n0?J:Z)(t)},et=Math.min,it=function(t){return t>0?et(tt(t),9007199254740991):0},ot=Math.max,nt=Math.min,rt=function(t,e){var i=tt(t);return i<0?ot(i+e,0):nt(i,e)},st=function(t){return function(e,i,o){var n,r=g(e),s=it(r.length),a=rt(o,s);if(t&&i!=i){for(;s>a;)if((n=r[a++])!=n)return!0}else for(;s>a;a++)if((t||a in r)&&r[a]===i)return t||a||0;return!t&&-1}},at={includes:st(!0),indexOf:st(!1)},ht={},lt=at.indexOf,dt=function(t,e){var i,o=g(t),n=0,r=[];for(i in o)!w(ht,i)&&w(o,i)&&r.push(i);for(;e.length>n;)w(o,i=e[n++])&&(~lt(r,i)||r.push(i));return r},ct=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],ut=Object.keys||function(t){return dt(t,ct)},ft={f:Object.getOwnPropertySymbols},pt=function(t){return Object(v(t))},vt=Object.assign,gt=Object.defineProperty,yt=!vt||r((function(){if(s&&1!==vt({b:1},vt(gt({},"a",{enumerable:!0,get:function(){gt(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},i=Symbol();return t[i]=7,"abcdefghijklmnopqrst".split("").forEach((function(t){e[t]=t})),7!=vt({},t)[i]||"abcdefghijklmnopqrst"!=ut(vt({},e)).join("")}))?function(t,e){for(var i=pt(t),o=arguments.length,n=1,r=ft.f,a=l.f;o>n;)for(var h,d=p(arguments[n++]),c=r?ut(d).concat(r(d)):ut(d),u=c.length,f=0;u>f;)h=c[f++],s&&!a.call(d,h)||(i[h]=d[h]);return i}:vt;q({target:"Object",stat:!0,forced:Object.assign!==yt},{assign:yt});var mt=z.Object.assign;function bt(t,e,i,o){t.beginPath(),t.arc(e,i,o,0,2*Math.PI,!1),t.closePath()}function wt(t,e,i,o,n,r){var s=Math.PI/180;o-2*r<0&&(r=o/2),n-2*r<0&&(r=n/2),t.beginPath(),t.moveTo(e+r,i),t.lineTo(e+o-r,i),t.arc(e+o-r,i+r,r,270*s,360*s,!1),t.lineTo(e+o,i+n-r),t.arc(e+o-r,i+n-r,r,0,90*s,!1),t.lineTo(e+r,i+n),t.arc(e+r,i+n-r,r,90*s,180*s,!1),t.lineTo(e,i+r),t.arc(e+r,i+r,r,180*s,270*s,!1),t.closePath()}function kt(t,e,i,o,n){var r=o/2*.5522848,s=n/2*.5522848,a=e+o,h=i+n,l=e+o/2,d=i+n/2;t.beginPath(),t.moveTo(e,d),t.bezierCurveTo(e,d-s,l-r,i,l,i),t.bezierCurveTo(l+r,i,a,d-s,a,d),t.bezierCurveTo(a,d+s,l+r,h,l,h),t.bezierCurveTo(l-r,h,e,d+s,e,d),t.closePath()}function _t(t,e,i,o,n){var r=n*(1/3),s=o/2*.5522848,a=r/2*.5522848,h=e+o,l=i+r,d=e+o/2,c=i+r/2,u=i+(n-r/2),f=i+n;t.beginPath(),t.moveTo(h,c),t.bezierCurveTo(h,c+a,d+s,l,d,l),t.bezierCurveTo(d-s,l,e,c+a,e,c),t.bezierCurveTo(e,c-a,d-s,i,d,i),t.bezierCurveTo(d+s,i,h,c-a,h,c),t.lineTo(h,u),t.bezierCurveTo(h,u+a,d+s,f,d,f),t.bezierCurveTo(d-s,f,e,u+a,e,u),t.lineTo(e,c)}function xt(t,e,i,o,n,r){t.beginPath(),t.moveTo(e,i);for(var s=r.length,a=o-e,h=n-i,l=h/a,d=Math.sqrt(a*a+h*h),c=0,u=!0,f=0,p=+r[0];d>=.1;)(p=+r[c++%s])>d&&(p=d),f=Math.sqrt(p*p/(1+l*l)),e+=f=a<0?-f:f,i+=l*f,!0===u?t.lineTo(e,i):t.moveTo(e,i),d-=p,u=!u}var Ot={circle:bt,dashedLine:xt,database:_t,diamond:function(t,e,i,o){t.beginPath(),t.lineTo(e,i+o),t.lineTo(e+o,i),t.lineTo(e,i-o),t.lineTo(e-o,i),t.closePath()},ellipse:kt,ellipse_vis:kt,hexagon:function(t,e,i,o){t.beginPath();var n=2*Math.PI/6;t.moveTo(e+o,i);for(var r=1;r<6;r++)t.lineTo(e+o*Math.cos(n*r),i+o*Math.sin(n*r));t.closePath()},roundRect:wt,square:function(t,e,i,o){t.beginPath(),t.rect(e-o,i-o,2*o,2*o),t.closePath()},star:function(t,e,i,o){t.beginPath(),i+=.1*(o*=.82);for(var n=0;n<10;n++){var r=n%2==0?1.3*o:.5*o;t.lineTo(e+r*Math.sin(2*n*Math.PI/10),i-r*Math.cos(2*n*Math.PI/10))}t.closePath()},triangle:function(t,e,i,o){t.beginPath(),i+=.275*(o*=1.15);var n=2*o,r=n/2,s=Math.sqrt(3)/6*n,a=Math.sqrt(n*n-r*r);t.moveTo(e,i-(a-s)),t.lineTo(e+r,i+s),t.lineTo(e-r,i+s),t.lineTo(e,i-(a-s)),t.closePath()},triangleDown:function(t,e,i,o){t.beginPath(),i-=.275*(o*=1.15);var n=2*o,r=n/2,s=Math.sqrt(3)/6*n,a=Math.sqrt(n*n-r*r);t.moveTo(e,i+(a-s)),t.lineTo(e+r,i-s),t.lineTo(e-r,i-s),t.lineTo(e,i+(a-s)),t.closePath()}};var Et=i((function(t){function e(t){if(t)return function(t){for(var i in e.prototype)t[i]=e.prototype[i];return t}(t)}t.exports=e,e.prototype.on=e.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},e.prototype.once=function(t,e){function i(){this.off(t,i),e.apply(this,arguments)}return i.fn=e,this.on(t,i),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var i,o=this._callbacks["$"+t];if(!o)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var n=0;nr;)R.f(t,i=o[r++],e[i]);return t};q({target:"Object",stat:!0,forced:!s,sham:!s},{defineProperties:Tt});var Dt=i((function(t){var e=z.Object,i=t.exports=function(t,i){return e.defineProperties(t,i)};e.defineProperties.sham&&(i.sham=!0)})),Mt=function(t){return"function"==typeof t?t:void 0},Pt=function(t,e){return arguments.length<2?Mt(z[t])||Mt(n[t]):z[t]&&z[t][e]||n[t]&&n[t][e]},It=ct.concat("length","prototype"),Bt={f:Object.getOwnPropertyNames||function(t){return dt(t,It)}},zt=Pt("Reflect","ownKeys")||function(t){var e=Bt.f(A(t)),i=ft.f;return i?e.concat(i(t)):e},Ft=function(t,e,i){var o=m(e);o in t?R.f(t,o,d(0,i)):t[o]=i};q({target:"Object",stat:!0,sham:!s},{getOwnPropertyDescriptors:function(t){for(var e,i,o=g(t),n=C.f,r=zt(o),s={},a=0;r.length>a;)void 0!==(i=n(o,e=r[a++]))&&Ft(s,e,i);return s}});var Nt=z.Object.getOwnPropertyDescriptors,At=C.f,jt=r((function(){At(1)}));q({target:"Object",stat:!0,forced:!s||jt,sham:!s},{getOwnPropertyDescriptor:function(t,e){return At(g(t),e)}});var Rt,Lt=i((function(t){var e=z.Object,i=t.exports=function(t,i){return e.getOwnPropertyDescriptor(t,i)};e.getOwnPropertyDescriptor.sham&&(i.sham=!0)})),Ht=Lt,Wt=!!Object.getOwnPropertySymbols&&!r((function(){return!String(Symbol())})),qt=Wt&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,Vt=Array.isArray||function(t){return"Array"==u(t)},Ut=Pt("document","documentElement"),Yt=n["__core-js_shared__"]||function(t,e){try{L(n,t,e)}catch(i){n[t]=e}return e}("__core-js_shared__",{}),Xt=i((function(t){(t.exports=function(t,e){return Yt[t]||(Yt[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.6.4",mode:"pure",copyright:"© 2020 Denis Pushkarev (zloirock.ru)"})})),Gt=0,Kt=Math.random(),$t=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++Gt+Kt).toString(36)},Qt=Xt("keys"),Zt=function(t){return Qt[t]||(Qt[t]=$t(t))},Jt=Zt("IE_PROTO"),te=function(){},ee=function(t){return"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+