[
  {
    "path": ".gitignore",
    "content": "particles.min.js"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015, Vincent Garreau\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "## particles.js\n\n### A lightweight JavaScript library for creating particles.\n\n------------------------------\n### `Demo / Generator`\n\n<a href=\"http://vincentgarreau.com/particles.js/\" target=\"_blank\"><img src=\"http://vincentgarreau.com/particles.js/assets/img/github-screen.jpg\" alt=\"particles.js generator\" /></a>\n\nConfigure, export, and share your particles.js configuration on CodePen: <br />\nhttp://vincentgarreau.com/particles.js/\n\nCodePen demo: <br />\nhttp://codepen.io/VincentGarreau/pen/pnlso\n\n-------------------------------\n### `Usage`\n\nLoad particles.js and configure the particles:\n\n**index.html**\n```html\n<div id=\"particles-js\"></div>\n\n<script src=\"particles.js\"></script>\n```\n\n**app.js**\n```javascript\n/* particlesJS.load(@dom-id, @path-json, @callback (optional)); */\nparticlesJS.load('particles-js', 'assets/particles.json', function() {\n  console.log('callback - particles.js config loaded');\n});\n```\n\n**particles.json**\n```javascript\n{\n  \"particles\": {\n    \"number\": {\n      \"value\": 80,\n      \"density\": {\n        \"enable\": true,\n        \"value_area\": 800\n      }\n    },\n    \"color\": {\n      \"value\": \"#ffffff\"\n    },\n    \"shape\": {\n      \"type\": \"circle\",\n      \"stroke\": {\n        \"width\": 0,\n        \"color\": \"#000000\"\n      },\n      \"polygon\": {\n        \"nb_sides\": 5\n      },\n      \"image\": {\n        \"src\": \"img/github.svg\",\n        \"width\": 100,\n        \"height\": 100\n      }\n    },\n    \"opacity\": {\n      \"value\": 0.5,\n      \"random\": false,\n      \"anim\": {\n        \"enable\": false,\n        \"speed\": 1,\n        \"opacity_min\": 0.1,\n        \"sync\": false\n      }\n    },\n    \"size\": {\n      \"value\": 10,\n      \"random\": true,\n      \"anim\": {\n        \"enable\": false,\n        \"speed\": 80,\n        \"size_min\": 0.1,\n        \"sync\": false\n      }\n    },\n    \"line_linked\": {\n      \"enable\": true,\n      \"distance\": 300,\n      \"color\": \"#ffffff\",\n      \"opacity\": 0.4,\n      \"width\": 2\n    },\n    \"move\": {\n      \"enable\": true,\n      \"speed\": 12,\n      \"direction\": \"none\",\n      \"random\": false,\n      \"straight\": false,\n      \"out_mode\": \"out\",\n      \"bounce\": false,\n      \"attract\": {\n        \"enable\": false,\n        \"rotateX\": 600,\n        \"rotateY\": 1200\n      }\n    }\n  },\n  \"interactivity\": {\n    \"detect_on\": \"canvas\",\n    \"events\": {\n      \"onhover\": {\n        \"enable\": false,\n        \"mode\": \"repulse\"\n      },\n      \"onclick\": {\n        \"enable\": true,\n        \"mode\": \"push\"\n      },\n      \"resize\": true\n    },\n    \"modes\": {\n      \"grab\": {\n        \"distance\": 800,\n        \"line_linked\": {\n          \"opacity\": 1\n        }\n      },\n      \"bubble\": {\n        \"distance\": 800,\n        \"size\": 80,\n        \"duration\": 2,\n        \"opacity\": 0.8,\n        \"speed\": 3\n      },\n      \"repulse\": {\n        \"distance\": 400,\n        \"duration\": 0.4\n      },\n      \"push\": {\n        \"particles_nb\": 4\n      },\n      \"remove\": {\n        \"particles_nb\": 2\n      }\n    }\n  },\n  \"retina_detect\": true\n}\n```\n\n-------------------------------\n\n### `Options`\n\nkey | option type / notes | example\n----|---------|------\n`particles.number.value` | number | `40`\n`particles.number.density.enable` | boolean | `true` / `false` \n`particles.number.density.value_area` | number | `800`\n`particles.color.value` | HEX (string) <br /> RGB (object) <br /> HSL (object) <br /> array selection (HEX) <br /> random (string) | `\"#b61924\"` <br /> `{r:182, g:25, b:36}` <br />  `{h:356, s:76, l:41}` <br /> `[\"#b61924\", \"#333333\", \"999999\"]` <br /> `\"random\"`\n`particles.shape.type` | string <br /> array selection | `\"circle\"` <br /> `\"edge\"` <br /> `\"triangle\"` <br /> `\"polygon\"` <br /> `\"star\"` <br /> `\"image\"` <br /> `[\"circle\", \"triangle\", \"image\"]`\n`particles.shape.stroke.width` | number | `2`\n`particles.shape.stroke.color` | HEX (string) | `\"#222222\"`\n`particles.shape.polygon.nb_slides` | number | `5`\n`particles.shape.image.src` | path link <br /> svg / png / gif / jpg | `\"assets/img/yop.svg\"` <br /> `\"http://mywebsite.com/assets/img/yop.png\"`\n`particles.shape.image.width` | number <br />(for aspect ratio) | `100`\n`particles.shape.image.height` | number <br />(for aspect ratio) | `100`\n`particles.opacity.value` | number (0 to 1) | `0.75`\n`particles.opacity.random` | boolean | `true` / `false` \n`particles.opacity.anim.enable` | boolean | `true` / `false` \n`particles.opacity.anim.speed` | number | `3`\n`particles.opacity.anim.opacity_min` | number (0 to 1) | `0.25`\n`particles.opacity.anim.sync` | boolean | `true` / `false`\n`particles.size.value` | number | `20`\n`particles.size.random` | boolean | `true` / `false` \n`particles.size.anim.enable` | boolean | `true` / `false` \n`particles.size.anim.speed` | number | `3`\n`particles.size.anim.size_min` | number | `0.25`\n`particles.size.anim.sync` | boolean | `true` / `false`\n`particles.line_linked.enable` | boolean | `true` / `false`\n`particles.line_linked.distance` | number | `150`\n`particles.line_linked.color` | HEX (string) | `#ffffff`\n`particles.line_linked.opacity` | number (0 to 1) | `0.5`\n`particles.line_linked.width` | number | `1.5`\n`particles.move.enable` | boolean | `true` / `false`\n`particles.move.speed` | number | `4`\n`particles.move.direction` | string | `\"none\"` <br /> `\"top\"` <br /> `\"top-right\"` <br /> `\"right\"` <br /> `\"bottom-right\"` <br /> `\"bottom\"` <br /> `\"bottom-left\"` <br /> `\"left\"` <br /> `\"top-left\"`\n`particles.move.random` | boolean | `true` / `false`\n`particles.move.straight` | boolean | `true` / `false`\n`particles.move.out_mode` | string <br /> (out of canvas) | `\"out\"` <br /> `\"bounce\"`\n`particles.move.bounce` | boolean <br /> (between particles) | `true` / `false`\n`particles.move.attract.enable` | boolean | `true` / `false`\n`particles.move.attract.rotateX` | number | `3000`\n`particles.move.attract.rotateY` | number | `1500`\n`interactivity.detect_on` | string | `\"canvas\", \"window\"`\n`interactivity.events.onhover.enable` | boolean | `true` / `false`\n`interactivity.events.onhover.mode` | string <br /> array selection | `\"grab\"` <br /> `\"bubble\"` <br /> `\"repulse\"` <br /> `[\"grab\", \"bubble\"]`\n`interactivity.events.onclick.enable` | boolean | `true` / `false`\n`interactivity.events.onclick.mode` | string <br /> array selection | `\"push\"` <br /> `\"remove\"` <br /> `\"bubble\"` <br /> `\"repulse\"` <br /> `[\"push\", \"repulse\"]`\n`interactivity.events.resize` | boolean | `true` / `false`\n`interactivity.events.modes.grab.distance` | number | `100`\n`interactivity.events.modes.grab.line_linked.opacity` | number (0 to 1) | `0.75`\n`interactivity.events.modes.bubble.distance` | number | `100`\n`interactivity.events.modes.bubble.size` | number | `40`\n`interactivity.events.modes.bubble.duration` | number <br /> (second) | `0.4`\n`interactivity.events.modes.repulse.distance` | number | `200`\n`interactivity.events.modes.repulse.duration` | number <br /> (second) | `1.2`\n`interactivity.events.modes.push.particles_nb` | number | `4`\n`interactivity.events.modes.push.particles_nb` | number | `4`\n`retina_detect` | boolean | `true` / `false`\n\n-------------------------------\n\n### `Packages install`\n\n##### ***npm***\nhttps://www.npmjs.com/package/particles.js\n```\nnpm install particles.js\n```\n\n##### ***Bower***\n```\nbower install particles.js --save\n```\n\n##### ***Rails Assets***\n```\ngem 'rails-assets-particles.js'\n```\n\n##### ***Meteor***\nhttps://atmospherejs.com/newswim/particles\n```\nmeteor add newswim:particles\n```\n\n-------------------------------\n\n### `Hosting / CDN`\n\n***Please use this host or your own to load particles.js on your projects***\n\nhttp://www.jsdelivr.com/#!particles.js\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"particles.js\",\n  \"main\": \"particles.js\",\n  \"homepage\": \"https://github.com/VincentGarreau/particles.js\",\n  \"authors\": [\n    \"Vincent Garreau <vin.garreau@gmail.com>\"\n  ],\n  \"description\": \"A lightweight JavaScript library for creating particles.\",\n  \"keywords\": [\n    \"particle\",\n    \"particles\"\n  ],\n  \"license\": \"MIT\",\n  \"ignore\": [\n    \"**/.*\",\n    \"node_modules\",\n    \"bower_components\",\n    \"test\",\n    \"tests\"\n  ]\n}\n"
  },
  {
    "path": "demo/css/style.css",
    "content": "/* =============================================================================\n   HTML5 CSS Reset Minified - Eric Meyer\n   ========================================================================== */\n\nhtml,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,figcaption,figure,footer,header,hgroup,menu,nav,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}\nbody{line-height:1}\narticle,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}\nnav ul{list-style:none}\nblockquote,q{quotes:none}\nblockquote:before,blockquote:after,q:before,q:after{content:none}\na{margin:0;padding:0;font-size:100%;vertical-align:baseline;background:transparent;text-decoration:none}\nmark{background-color:#ff9;color:#000;font-style:italic;font-weight:bold}\ndel{text-decoration:line-through}\nabbr[title],dfn[title]{border-bottom:1px dotted;cursor:help}\ntable{border-collapse:collapse;border-spacing:0}\nhr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}\ninput,select{vertical-align:middle}\nli{list-style:none}\n\n\n/* =============================================================================\n   My CSS\n   ========================================================================== */\n\n/* ---- base ---- */\n\nhtml,body{ \n\twidth:100%;\n\theight:100%;\n\tbackground:#111;\n}\n\nhtml{\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nbody{\n  font:normal 75% Arial, Helvetica, sans-serif;\n}\n\ncanvas{\n  display:block;\n  vertical-align:bottom;\n}\n\n\n/* ---- stats.js ---- */\n\n.count-particles{\n  background: #000022;\n  position: absolute;\n  top: 48px;\n  left: 0;\n  width: 80px;\n  color: #13E8E9;\n  font-size: .8em;\n  text-align: left;\n  text-indent: 4px;\n  line-height: 14px;\n  padding-bottom: 2px;\n  font-family: Helvetica, Arial, sans-serif;\n  font-weight: bold;\n}\n\n.js-count-particles{\n  font-size: 1.1em;\n}\n\n#stats,\n.count-particles{\n  -webkit-user-select: none;\n  margin-top: 5px;\n  margin-left: 5px;\n}\n\n#stats{\n  border-radius: 3px 3px 0 0;\n  overflow: hidden;\n}\n\n.count-particles{\n  border-radius: 0 0 3px 3px;\n}\n\n\n/* ---- particles.js container ---- */\n\n#particles-js{\n  width: 100%;\n  height: 100%;\n  background-color: #b61924;\n  background-image: url('');\n  background-size: cover;\n  background-position: 50% 50%;\n  background-repeat: no-repeat;\n}\n"
  },
  {
    "path": "demo/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>particles.js</title>\n  <meta name=\"description\" content=\"particles.js is a lightweight JavaScript library for creating particles.\">\n  <meta name=\"author\" content=\"Vincent Garreau\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n  <link rel=\"stylesheet\" media=\"screen\" href=\"css/style.css\">\n</head>\n<body>\n\n<!-- count particles -->\n<div class=\"count-particles\">\n  <span class=\"js-count-particles\">--</span> particles\n</div>\n\n<!-- particles.js container -->\n<div id=\"particles-js\"></div>\n\n<!-- scripts -->\n<script src=\"../particles.js\"></script>\n<script src=\"js/app.js\"></script>\n\n<!-- stats.js -->\n<script src=\"js/lib/stats.js\"></script>\n<script>\n  var count_particles, stats, update;\n  stats = new Stats;\n  stats.setMode(0);\n  stats.domElement.style.position = 'absolute';\n  stats.domElement.style.left = '0px';\n  stats.domElement.style.top = '0px';\n  document.body.appendChild(stats.domElement);\n  count_particles = document.querySelector('.js-count-particles');\n  update = function() {\n    stats.begin();\n    stats.end();\n    if (window.pJSDom[0].pJS.particles && window.pJSDom[0].pJS.particles.array) {\n      count_particles.innerText = window.pJSDom[0].pJS.particles.array.length;\n    }\n    requestAnimationFrame(update);\n  };\n  requestAnimationFrame(update);\n</script>\n\n</body>\n</html>"
  },
  {
    "path": "demo/js/app.js",
    "content": "/* -----------------------------------------------\n/* How to use? : Check the GitHub README\n/* ----------------------------------------------- */\n\n/* To load a config file (particles.json) you need to host this demo (MAMP/WAMP/local)... */\n/*\nparticlesJS.load('particles-js', 'particles.json', function() {\n  console.log('particles.js loaded - callback');\n});\n*/\n\n/* Otherwise just put the config content (json): */\n\nparticlesJS('particles-js',\n  \n  {\n    \"particles\": {\n      \"number\": {\n        \"value\": 80,\n        \"density\": {\n          \"enable\": true,\n          \"value_area\": 800\n        }\n      },\n      \"color\": {\n        \"value\": \"#ffffff\"\n      },\n      \"shape\": {\n        \"type\": \"circle\",\n        \"stroke\": {\n          \"width\": 0,\n          \"color\": \"#000000\"\n        },\n        \"polygon\": {\n          \"nb_sides\": 5\n        },\n        \"image\": {\n          \"src\": \"img/github.svg\",\n          \"width\": 100,\n          \"height\": 100\n        }\n      },\n      \"opacity\": {\n        \"value\": 0.5,\n        \"random\": false,\n        \"anim\": {\n          \"enable\": false,\n          \"speed\": 1,\n          \"opacity_min\": 0.1,\n          \"sync\": false\n        }\n      },\n      \"size\": {\n        \"value\": 5,\n        \"random\": true,\n        \"anim\": {\n          \"enable\": false,\n          \"speed\": 40,\n          \"size_min\": 0.1,\n          \"sync\": false\n        }\n      },\n      \"line_linked\": {\n        \"enable\": true,\n        \"distance\": 150,\n        \"color\": \"#ffffff\",\n        \"opacity\": 0.4,\n        \"width\": 1\n      },\n      \"move\": {\n        \"enable\": true,\n        \"speed\": 6,\n        \"direction\": \"none\",\n        \"random\": false,\n        \"straight\": false,\n        \"out_mode\": \"out\",\n        \"attract\": {\n          \"enable\": false,\n          \"rotateX\": 600,\n          \"rotateY\": 1200\n        }\n      }\n    },\n    \"interactivity\": {\n      \"detect_on\": \"canvas\",\n      \"events\": {\n        \"onhover\": {\n          \"enable\": true,\n          \"mode\": \"repulse\"\n        },\n        \"onclick\": {\n          \"enable\": true,\n          \"mode\": \"push\"\n        },\n        \"resize\": true\n      },\n      \"modes\": {\n        \"grab\": {\n          \"distance\": 400,\n          \"line_linked\": {\n            \"opacity\": 1\n          }\n        },\n        \"bubble\": {\n          \"distance\": 400,\n          \"size\": 40,\n          \"duration\": 2,\n          \"opacity\": 8,\n          \"speed\": 3\n        },\n        \"repulse\": {\n          \"distance\": 200\n        },\n        \"push\": {\n          \"particles_nb\": 4\n        },\n        \"remove\": {\n          \"particles_nb\": 2\n        }\n      }\n    },\n    \"retina_detect\": true,\n    \"config_demo\": {\n      \"hide_card\": false,\n      \"background_color\": \"#b61924\",\n      \"background_image\": \"\",\n      \"background_position\": \"50% 50%\",\n      \"background_repeat\": \"no-repeat\",\n      \"background_size\": \"cover\"\n    }\n  }\n\n);"
  },
  {
    "path": "demo/js/lib/stats.js",
    "content": "/**\n * @author mrdoob / http://mrdoob.com/\n */\n\nvar Stats = function () {\n\n\tvar startTime = Date.now(), prevTime = startTime;\n\tvar ms = 0, msMin = Infinity, msMax = 0;\n\tvar fps = 0, fpsMin = Infinity, fpsMax = 0;\n\tvar frames = 0, mode = 0;\n\n\tvar container = document.createElement( 'div' );\n\tcontainer.id = 'stats';\n\tcontainer.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false );\n\tcontainer.style.cssText = 'width:80px;opacity:0.9;cursor:pointer';\n\n\tvar fpsDiv = document.createElement( 'div' );\n\tfpsDiv.id = 'fps';\n\tfpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002';\n\tcontainer.appendChild( fpsDiv );\n\n\tvar fpsText = document.createElement( 'div' );\n\tfpsText.id = 'fpsText';\n\tfpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';\n\tfpsText.innerHTML = 'FPS';\n\tfpsDiv.appendChild( fpsText );\n\n\tvar fpsGraph = document.createElement( 'div' );\n\tfpsGraph.id = 'fpsGraph';\n\tfpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff';\n\tfpsDiv.appendChild( fpsGraph );\n\n\twhile ( fpsGraph.children.length < 74 ) {\n\n\t\tvar bar = document.createElement( 'span' );\n\t\tbar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113';\n\t\tfpsGraph.appendChild( bar );\n\n\t}\n\n\tvar msDiv = document.createElement( 'div' );\n\tmsDiv.id = 'ms';\n\tmsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none';\n\tcontainer.appendChild( msDiv );\n\n\tvar msText = document.createElement( 'div' );\n\tmsText.id = 'msText';\n\tmsText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';\n\tmsText.innerHTML = 'MS';\n\tmsDiv.appendChild( msText );\n\n\tvar msGraph = document.createElement( 'div' );\n\tmsGraph.id = 'msGraph';\n\tmsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0';\n\tmsDiv.appendChild( msGraph );\n\n\twhile ( msGraph.children.length < 74 ) {\n\n\t\tvar bar = document.createElement( 'span' );\n\t\tbar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131';\n\t\tmsGraph.appendChild( bar );\n\n\t}\n\n\tvar setMode = function ( value ) {\n\n\t\tmode = value;\n\n\t\tswitch ( mode ) {\n\n\t\t\tcase 0:\n\t\t\t\tfpsDiv.style.display = 'block';\n\t\t\t\tmsDiv.style.display = 'none';\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tfpsDiv.style.display = 'none';\n\t\t\t\tmsDiv.style.display = 'block';\n\t\t\t\tbreak;\n\t\t}\n\n\t};\n\n\tvar updateGraph = function ( dom, value ) {\n\n\t\tvar child = dom.appendChild( dom.firstChild );\n\t\tchild.style.height = value + 'px';\n\n\t};\n\n\treturn {\n\n\t\tREVISION: 12,\n\n\t\tdomElement: container,\n\n\t\tsetMode: setMode,\n\n\t\tbegin: function () {\n\n\t\t\tstartTime = Date.now();\n\n\t\t},\n\n\t\tend: function () {\n\n\t\t\tvar time = Date.now();\n\n\t\t\tms = time - startTime;\n\t\t\tmsMin = Math.min( msMin, ms );\n\t\t\tmsMax = Math.max( msMax, ms );\n\n\t\t\tmsText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')';\n\t\t\tupdateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) );\n\n\t\t\tframes ++;\n\n\t\t\tif ( time > prevTime + 1000 ) {\n\n\t\t\t\tfps = Math.round( ( frames * 1000 ) / ( time - prevTime ) );\n\t\t\t\tfpsMin = Math.min( fpsMin, fps );\n\t\t\t\tfpsMax = Math.max( fpsMax, fps );\n\n\t\t\t\tfpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')';\n\t\t\t\tupdateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) );\n\n\t\t\t\tprevTime = time;\n\t\t\t\tframes = 0;\n\n\t\t\t}\n\n\t\t\treturn time;\n\n\t\t},\n\n\t\tupdate: function () {\n\n\t\t\tstartTime = this.end();\n\n\t\t}\n\n\t}\n\n};\n\nif ( typeof module === 'object' ) {\n\n\tmodule.exports = Stats;\n\n}"
  },
  {
    "path": "demo/particles.json",
    "content": "{\n  \"particles\": {\n    \"number\": {\n      \"value\": 80,\n      \"density\": {\n        \"enable\": true,\n        \"value_area\": 800\n      }\n    },\n    \"color\": {\n      \"value\": \"#ffffff\"\n    },\n    \"shape\": {\n      \"type\": \"circle\",\n      \"stroke\": {\n        \"width\": 0,\n        \"color\": \"#000000\"\n      },\n      \"polygon\": {\n        \"nb_sides\": 5\n      },\n      \"image\": {\n        \"src\": \"img/github.svg\",\n        \"width\": 100,\n        \"height\": 100\n      }\n    },\n    \"opacity\": {\n      \"value\": 0.5,\n      \"random\": false,\n      \"anim\": {\n        \"enable\": false,\n        \"speed\": 1,\n        \"opacity_min\": 0.1,\n        \"sync\": false\n      }\n    },\n    \"size\": {\n      \"value\": 5,\n      \"random\": true,\n      \"anim\": {\n        \"enable\": false,\n        \"speed\": 40,\n        \"size_min\": 0.1,\n        \"sync\": false\n      }\n    },\n    \"line_linked\": {\n      \"enable\": true,\n      \"distance\": 150,\n      \"color\": \"#ffffff\",\n      \"opacity\": 0.4,\n      \"width\": 1\n    },\n    \"move\": {\n      \"enable\": true,\n      \"speed\": 6,\n      \"direction\": \"none\",\n      \"random\": false,\n      \"straight\": false,\n      \"out_mode\": \"out\",\n      \"attract\": {\n        \"enable\": false,\n        \"rotateX\": 600,\n        \"rotateY\": 1200\n      }\n    }\n  },\n  \"interactivity\": {\n    \"detect_on\": \"canvas\",\n    \"events\": {\n      \"onhover\": {\n        \"enable\": true,\n        \"mode\": \"repulse\"\n      },\n      \"onclick\": {\n        \"enable\": true,\n        \"mode\": \"push\"\n      },\n      \"resize\": true\n    },\n    \"modes\": {\n      \"grab\": {\n        \"distance\": 400,\n        \"line_linked\": {\n          \"opacity\": 1\n        }\n      },\n      \"bubble\": {\n        \"distance\": 400,\n        \"size\": 40,\n        \"duration\": 2,\n        \"opacity\": 8,\n        \"speed\": 3\n      },\n      \"repulse\": {\n        \"distance\": 200\n      },\n      \"push\": {\n        \"particles_nb\": 4\n      },\n      \"remove\": {\n        \"particles_nb\": 2\n      }\n    }\n  },\n  \"retina_detect\": true,\n  \"config_demo\": {\n    \"hide_card\": false,\n    \"background_color\": \"#b61924\",\n    \"background_image\": \"\",\n    \"background_position\": \"50% 50%\",\n    \"background_repeat\": \"no-repeat\",\n    \"background_size\": \"cover\"\n  }\n}"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"particles.js\",\n  \"version\": \"2.0.0\",\n  \"description\": \"A lightweight JavaScript library for creating particles\",\n  \"homepage\": \"http://vincentgarreau.com/particles.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/VincentGarreau/particles.js.git\"\n  },\n  \"keywords\": [\n    \"particles\",\n    \"particle\",\n    \"canvas\"\n  ],\n  \"author\": \"Vincent Garreau\",\n  \"license\": \"MIT\",\n  \"files\": [\n    \"particles.js\",\n    \"particles.min.js\"\n  ],\n  \"bugs\": {\n    \"url\": \"https://github.com/VincentGarreau/particles.js/issues\"\n  },\n  \"homepage\": \"https://github.com/VincentGarreau/particles.js\"\n}"
  },
  {
    "path": "particles.js",
    "content": "/* -----------------------------------------------\n/* Author : Vincent Garreau  - vincentgarreau.com\n/* MIT license: http://opensource.org/licenses/MIT\n/* Demo / Generator : vincentgarreau.com/particles.js\n/* GitHub : github.com/VincentGarreau/particles.js\n/* How to use? : Check the GitHub README\n/* v2.0.0\n/* ----------------------------------------------- */\n\nvar pJS = function(tag_id, params){\n\n  var canvas_el = document.querySelector('#'+tag_id+' > .particles-js-canvas-el');\n\n  /* particles.js variables with default values */\n  this.pJS = {\n    canvas: {\n      el: canvas_el,\n      w: canvas_el.offsetWidth,\n      h: canvas_el.offsetHeight\n    },\n    particles: {\n      number: {\n        value: 400,\n        density: {\n          enable: true,\n          value_area: 800\n        }\n      },\n      color: {\n        value: '#fff'\n      },\n      shape: {\n        type: 'circle',\n        stroke: {\n          width: 0,\n          color: '#ff0000'\n        },\n        polygon: {\n          nb_sides: 5\n        },\n        image: {\n          src: '',\n          width: 100,\n          height: 100\n        }\n      },\n      opacity: {\n        value: 1,\n        random: false,\n        anim: {\n          enable: false,\n          speed: 2,\n          opacity_min: 0,\n          sync: false\n        }\n      },\n      size: {\n        value: 20,\n        random: false,\n        anim: {\n          enable: false,\n          speed: 20,\n          size_min: 0,\n          sync: false\n        }\n      },\n      line_linked: {\n        enable: true,\n        distance: 100,\n        color: '#fff',\n        opacity: 1,\n        width: 1\n      },\n      move: {\n        enable: true,\n        speed: 2,\n        direction: 'none',\n        random: false,\n        straight: false,\n        out_mode: 'out',\n        bounce: false,\n        attract: {\n          enable: false,\n          rotateX: 3000,\n          rotateY: 3000\n        }\n      },\n      array: []\n    },\n    interactivity: {\n      detect_on: 'canvas',\n      events: {\n        onhover: {\n          enable: true,\n          mode: 'grab'\n        },\n        onclick: {\n          enable: true,\n          mode: 'push'\n        },\n        resize: true\n      },\n      modes: {\n        grab:{\n          distance: 100,\n          line_linked:{\n            opacity: 1\n          }\n        },\n        bubble:{\n          distance: 200,\n          size: 80,\n          duration: 0.4\n        },\n        repulse:{\n          distance: 200,\n          duration: 0.4\n        },\n        push:{\n          particles_nb: 4\n        },\n        remove:{\n          particles_nb: 2\n        }\n      },\n      mouse:{}\n    },\n    retina_detect: false,\n    fn: {\n      interact: {},\n      modes: {},\n      vendors:{}\n    },\n    tmp: {}\n  };\n\n  var pJS = this.pJS;\n\n  /* params settings */\n  if(params){\n    Object.deepExtend(pJS, params);\n  }\n\n  pJS.tmp.obj = {\n    size_value: pJS.particles.size.value,\n    size_anim_speed: pJS.particles.size.anim.speed,\n    move_speed: pJS.particles.move.speed,\n    line_linked_distance: pJS.particles.line_linked.distance,\n    line_linked_width: pJS.particles.line_linked.width,\n    mode_grab_distance: pJS.interactivity.modes.grab.distance,\n    mode_bubble_distance: pJS.interactivity.modes.bubble.distance,\n    mode_bubble_size: pJS.interactivity.modes.bubble.size,\n    mode_repulse_distance: pJS.interactivity.modes.repulse.distance\n  };\n\n\n  pJS.fn.retinaInit = function(){\n\n    if(pJS.retina_detect && window.devicePixelRatio > 1){\n      pJS.canvas.pxratio = window.devicePixelRatio; \n      pJS.tmp.retina = true;\n    } \n    else{\n      pJS.canvas.pxratio = 1;\n      pJS.tmp.retina = false;\n    }\n\n    pJS.canvas.w = pJS.canvas.el.offsetWidth * pJS.canvas.pxratio;\n    pJS.canvas.h = pJS.canvas.el.offsetHeight * pJS.canvas.pxratio;\n\n    pJS.particles.size.value = pJS.tmp.obj.size_value * pJS.canvas.pxratio;\n    pJS.particles.size.anim.speed = pJS.tmp.obj.size_anim_speed * pJS.canvas.pxratio;\n    pJS.particles.move.speed = pJS.tmp.obj.move_speed * pJS.canvas.pxratio;\n    pJS.particles.line_linked.distance = pJS.tmp.obj.line_linked_distance * pJS.canvas.pxratio;\n    pJS.interactivity.modes.grab.distance = pJS.tmp.obj.mode_grab_distance * pJS.canvas.pxratio;\n    pJS.interactivity.modes.bubble.distance = pJS.tmp.obj.mode_bubble_distance * pJS.canvas.pxratio;\n    pJS.particles.line_linked.width = pJS.tmp.obj.line_linked_width * pJS.canvas.pxratio;\n    pJS.interactivity.modes.bubble.size = pJS.tmp.obj.mode_bubble_size * pJS.canvas.pxratio;\n    pJS.interactivity.modes.repulse.distance = pJS.tmp.obj.mode_repulse_distance * pJS.canvas.pxratio;\n\n  };\n\n\n\n  /* ---------- pJS functions - canvas ------------ */\n\n  pJS.fn.canvasInit = function(){\n    pJS.canvas.ctx = pJS.canvas.el.getContext('2d');\n  };\n\n  pJS.fn.canvasSize = function(){\n\n    pJS.canvas.el.width = pJS.canvas.w;\n    pJS.canvas.el.height = pJS.canvas.h;\n\n    if(pJS && pJS.interactivity.events.resize){\n\n      window.addEventListener('resize', function(){\n\n          pJS.canvas.w = pJS.canvas.el.offsetWidth;\n          pJS.canvas.h = pJS.canvas.el.offsetHeight;\n\n          /* resize canvas */\n          if(pJS.tmp.retina){\n            pJS.canvas.w *= pJS.canvas.pxratio;\n            pJS.canvas.h *= pJS.canvas.pxratio;\n          }\n\n          pJS.canvas.el.width = pJS.canvas.w;\n          pJS.canvas.el.height = pJS.canvas.h;\n\n          /* repaint canvas on anim disabled */\n          if(!pJS.particles.move.enable){\n            pJS.fn.particlesEmpty();\n            pJS.fn.particlesCreate();\n            pJS.fn.particlesDraw();\n            pJS.fn.vendors.densityAutoParticles();\n          }\n\n        /* density particles enabled */\n        pJS.fn.vendors.densityAutoParticles();\n\n      });\n\n    }\n\n  };\n\n\n  pJS.fn.canvasPaint = function(){\n    pJS.canvas.ctx.fillRect(0, 0, pJS.canvas.w, pJS.canvas.h);\n  };\n\n  pJS.fn.canvasClear = function(){\n    pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h);\n  };\n\n\n  /* --------- pJS functions - particles ----------- */\n\n  pJS.fn.particle = function(color, opacity, position){\n\n    /* size */\n    this.radius = (pJS.particles.size.random ? Math.random() : 1) * pJS.particles.size.value;\n    if(pJS.particles.size.anim.enable){\n      this.size_status = false;\n      this.vs = pJS.particles.size.anim.speed / 100;\n      if(!pJS.particles.size.anim.sync){\n        this.vs = this.vs * Math.random();\n      }\n    }\n\n    /* position */\n    this.x = position ? position.x : Math.random() * pJS.canvas.w;\n    this.y = position ? position.y : Math.random() * pJS.canvas.h;\n\n    /* check position  - into the canvas */\n    if(this.x > pJS.canvas.w - this.radius*2) this.x = this.x - this.radius;\n    else if(this.x < this.radius*2) this.x = this.x + this.radius;\n    if(this.y > pJS.canvas.h - this.radius*2) this.y = this.y - this.radius;\n    else if(this.y < this.radius*2) this.y = this.y + this.radius;\n\n    /* check position - avoid overlap */\n    if(pJS.particles.move.bounce){\n      pJS.fn.vendors.checkOverlap(this, position);\n    }\n\n    /* color */\n    this.color = {};\n    if(typeof(color.value) == 'object'){\n\n      if(color.value instanceof Array){\n        var color_selected = color.value[Math.floor(Math.random() * pJS.particles.color.value.length)];\n        this.color.rgb = hexToRgb(color_selected);\n      }else{\n        if(color.value.r != undefined && color.value.g != undefined && color.value.b != undefined){\n          this.color.rgb = {\n            r: color.value.r,\n            g: color.value.g,\n            b: color.value.b\n          }\n        }\n        if(color.value.h != undefined && color.value.s != undefined && color.value.l != undefined){\n          this.color.hsl = {\n            h: color.value.h,\n            s: color.value.s,\n            l: color.value.l\n          }\n        }\n      }\n\n    }\n    else if(color.value == 'random'){\n      this.color.rgb = {\n        r: (Math.floor(Math.random() * (255 - 0 + 1)) + 0),\n        g: (Math.floor(Math.random() * (255 - 0 + 1)) + 0),\n        b: (Math.floor(Math.random() * (255 - 0 + 1)) + 0)\n      }\n    }\n    else if(typeof(color.value) == 'string'){\n      this.color = color;\n      this.color.rgb = hexToRgb(this.color.value);\n    }\n\n    /* opacity */\n    this.opacity = (pJS.particles.opacity.random ? Math.random() : 1) * pJS.particles.opacity.value;\n    if(pJS.particles.opacity.anim.enable){\n      this.opacity_status = false;\n      this.vo = pJS.particles.opacity.anim.speed / 100;\n      if(!pJS.particles.opacity.anim.sync){\n        this.vo = this.vo * Math.random();\n      }\n    }\n\n    /* animation - velocity for speed */\n    var velbase = {}\n    switch(pJS.particles.move.direction){\n      case 'top':\n        velbase = { x:0, y:-1 };\n      break;\n      case 'top-right':\n        velbase = { x:0.5, y:-0.5 };\n      break;\n      case 'right':\n        velbase = { x:1, y:-0 };\n      break;\n      case 'bottom-right':\n        velbase = { x:0.5, y:0.5 };\n      break;\n      case 'bottom':\n        velbase = { x:0, y:1 };\n      break;\n      case 'bottom-left':\n        velbase = { x:-0.5, y:1 };\n      break;\n      case 'left':\n        velbase = { x:-1, y:0 };\n      break;\n      case 'top-left':\n        velbase = { x:-0.5, y:-0.5 };\n      break;\n      default:\n        velbase = { x:0, y:0 };\n      break;\n    }\n\n    if(pJS.particles.move.straight){\n      this.vx = velbase.x;\n      this.vy = velbase.y;\n      if(pJS.particles.move.random){\n        this.vx = this.vx * (Math.random());\n        this.vy = this.vy * (Math.random());\n      }\n    }else{\n      this.vx = velbase.x + Math.random()-0.5;\n      this.vy = velbase.y + Math.random()-0.5;\n    }\n\n    // var theta = 2.0 * Math.PI * Math.random();\n    // this.vx = Math.cos(theta);\n    // this.vy = Math.sin(theta);\n\n    this.vx_i = this.vx;\n    this.vy_i = this.vy;\n\n    \n\n    /* if shape is image */\n\n    var shape_type = pJS.particles.shape.type;\n    if(typeof(shape_type) == 'object'){\n      if(shape_type instanceof Array){\n        var shape_selected = shape_type[Math.floor(Math.random() * shape_type.length)];\n        this.shape = shape_selected;\n      }\n    }else{\n      this.shape = shape_type;\n    }\n\n    if(this.shape == 'image'){\n      var sh = pJS.particles.shape;\n      this.img = {\n        src: sh.image.src,\n        ratio: sh.image.width / sh.image.height\n      }\n      if(!this.img.ratio) this.img.ratio = 1;\n      if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg != undefined){\n        pJS.fn.vendors.createSvgImg(this);\n        if(pJS.tmp.pushing){\n          this.img.loaded = false;\n        }\n      }\n    }\n\n    \n\n  };\n\n\n  pJS.fn.particle.prototype.draw = function() {\n\n    var p = this;\n\n    if(p.radius_bubble != undefined){\n      var radius = p.radius_bubble; \n    }else{\n      var radius = p.radius;\n    }\n\n    if(p.opacity_bubble != undefined){\n      var opacity = p.opacity_bubble;\n    }else{\n      var opacity = p.opacity;\n    }\n\n    if(p.color.rgb){\n      var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+opacity+')';\n    }else{\n      var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+opacity+')';\n    }\n\n    pJS.canvas.ctx.fillStyle = color_value;\n    pJS.canvas.ctx.beginPath();\n\n    switch(p.shape){\n\n      case 'circle':\n        pJS.canvas.ctx.arc(p.x, p.y, radius, 0, Math.PI * 2, false);\n      break;\n\n      case 'edge':\n        pJS.canvas.ctx.rect(p.x-radius, p.y-radius, radius*2, radius*2);\n      break;\n\n      case 'triangle':\n        pJS.fn.vendors.drawShape(pJS.canvas.ctx, p.x-radius, p.y+radius / 1.66, radius*2, 3, 2);\n      break;\n\n      case 'polygon':\n        pJS.fn.vendors.drawShape(\n          pJS.canvas.ctx,\n          p.x - radius / (pJS.particles.shape.polygon.nb_sides/3.5), // startX\n          p.y - radius / (2.66/3.5), // startY\n          radius*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength\n          pJS.particles.shape.polygon.nb_sides, // sideCountNumerator\n          1 // sideCountDenominator\n        );\n      break;\n\n      case 'star':\n        pJS.fn.vendors.drawShape(\n          pJS.canvas.ctx,\n          p.x - radius*2 / (pJS.particles.shape.polygon.nb_sides/4), // startX\n          p.y - radius / (2*2.66/3.5), // startY\n          radius*2*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength\n          pJS.particles.shape.polygon.nb_sides, // sideCountNumerator\n          2 // sideCountDenominator\n        );\n      break;\n\n      case 'image':\n\n        function draw(){\n          pJS.canvas.ctx.drawImage(\n            img_obj,\n            p.x-radius,\n            p.y-radius,\n            radius*2,\n            radius*2 / p.img.ratio\n          );\n        }\n\n        if(pJS.tmp.img_type == 'svg'){\n          var img_obj = p.img.obj;\n        }else{\n          var img_obj = pJS.tmp.img_obj;\n        }\n\n        if(img_obj){\n          draw();\n        }\n\n      break;\n\n    }\n\n    pJS.canvas.ctx.closePath();\n\n    if(pJS.particles.shape.stroke.width > 0){\n      pJS.canvas.ctx.strokeStyle = pJS.particles.shape.stroke.color;\n      pJS.canvas.ctx.lineWidth = pJS.particles.shape.stroke.width;\n      pJS.canvas.ctx.stroke();\n    }\n    \n    pJS.canvas.ctx.fill();\n    \n  };\n\n\n  pJS.fn.particlesCreate = function(){\n    for(var i = 0; i < pJS.particles.number.value; i++) {\n      pJS.particles.array.push(new pJS.fn.particle(pJS.particles.color, pJS.particles.opacity.value));\n    }\n  };\n\n  pJS.fn.particlesUpdate = function(){\n\n    for(var i = 0; i < pJS.particles.array.length; i++){\n\n      /* the particle */\n      var p = pJS.particles.array[i];\n\n      // var d = ( dx = pJS.interactivity.mouse.click_pos_x - p.x ) * dx + ( dy = pJS.interactivity.mouse.click_pos_y - p.y ) * dy;\n      // var f = -BANG_SIZE / d;\n      // if ( d < BANG_SIZE ) {\n      //     var t = Math.atan2( dy, dx );\n      //     p.vx = f * Math.cos(t);\n      //     p.vy = f * Math.sin(t);\n      // }\n\n      /* move the particle */\n      if(pJS.particles.move.enable){\n        var ms = pJS.particles.move.speed/2;\n        p.x += p.vx * ms;\n        p.y += p.vy * ms;\n      }\n\n      /* change opacity status */\n      if(pJS.particles.opacity.anim.enable) {\n        if(p.opacity_status == true) {\n          if(p.opacity >= pJS.particles.opacity.value) p.opacity_status = false;\n          p.opacity += p.vo;\n        }else {\n          if(p.opacity <= pJS.particles.opacity.anim.opacity_min) p.opacity_status = true;\n          p.opacity -= p.vo;\n        }\n        if(p.opacity < 0) p.opacity = 0;\n      }\n\n      /* change size */\n      if(pJS.particles.size.anim.enable){\n        if(p.size_status == true){\n          if(p.radius >= pJS.particles.size.value) p.size_status = false;\n          p.radius += p.vs;\n        }else{\n          if(p.radius <= pJS.particles.size.anim.size_min) p.size_status = true;\n          p.radius -= p.vs;\n        }\n        if(p.radius < 0) p.radius = 0;\n      }\n\n      /* change particle position if it is out of canvas */\n      if(pJS.particles.move.out_mode == 'bounce'){\n        var new_pos = {\n          x_left: p.radius,\n          x_right:  pJS.canvas.w,\n          y_top: p.radius,\n          y_bottom: pJS.canvas.h\n        }\n      }else{\n        var new_pos = {\n          x_left: -p.radius,\n          x_right: pJS.canvas.w + p.radius,\n          y_top: -p.radius,\n          y_bottom: pJS.canvas.h + p.radius\n        }\n      }\n\n      if(p.x - p.radius > pJS.canvas.w){\n        p.x = new_pos.x_left;\n        p.y = Math.random() * pJS.canvas.h;\n      }\n      else if(p.x + p.radius < 0){\n        p.x = new_pos.x_right;\n        p.y = Math.random() * pJS.canvas.h;\n      }\n      if(p.y - p.radius > pJS.canvas.h){\n        p.y = new_pos.y_top;\n        p.x = Math.random() * pJS.canvas.w;\n      }\n      else if(p.y + p.radius < 0){\n        p.y = new_pos.y_bottom;\n        p.x = Math.random() * pJS.canvas.w;\n      }\n\n      /* out of canvas modes */\n      switch(pJS.particles.move.out_mode){\n        case 'bounce':\n          if (p.x + p.radius > pJS.canvas.w) p.vx = -p.vx;\n          else if (p.x - p.radius < 0) p.vx = -p.vx;\n          if (p.y + p.radius > pJS.canvas.h) p.vy = -p.vy;\n          else if (p.y - p.radius < 0) p.vy = -p.vy;\n        break;\n      }\n\n      /* events */\n      if(isInArray('grab', pJS.interactivity.events.onhover.mode)){\n        pJS.fn.modes.grabParticle(p);\n      }\n\n      if(isInArray('bubble', pJS.interactivity.events.onhover.mode) || isInArray('bubble', pJS.interactivity.events.onclick.mode)){\n        pJS.fn.modes.bubbleParticle(p);\n      }\n\n      if(isInArray('repulse', pJS.interactivity.events.onhover.mode) || isInArray('repulse', pJS.interactivity.events.onclick.mode)){\n        pJS.fn.modes.repulseParticle(p);\n      }\n\n      /* interaction auto between particles */\n      if(pJS.particles.line_linked.enable || pJS.particles.move.attract.enable){\n        for(var j = i + 1; j < pJS.particles.array.length; j++){\n          var p2 = pJS.particles.array[j];\n\n          /* link particles */\n          if(pJS.particles.line_linked.enable){\n            pJS.fn.interact.linkParticles(p,p2);\n          }\n\n          /* attract particles */\n          if(pJS.particles.move.attract.enable){\n            pJS.fn.interact.attractParticles(p,p2);\n          }\n\n          /* bounce particles */\n          if(pJS.particles.move.bounce){\n            pJS.fn.interact.bounceParticles(p,p2);\n          }\n\n        }\n      }\n\n\n    }\n\n  };\n\n  pJS.fn.particlesDraw = function(){\n\n    /* clear canvas */\n    pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h);\n\n    /* update each particles param */\n    pJS.fn.particlesUpdate();\n\n    /* draw each particle */\n    for(var i = 0; i < pJS.particles.array.length; i++){\n      var p = pJS.particles.array[i];\n      p.draw();\n    }\n\n  };\n\n  pJS.fn.particlesEmpty = function(){\n    pJS.particles.array = [];\n  };\n\n  pJS.fn.particlesRefresh = function(){\n\n    /* init all */\n    cancelRequestAnimFrame(pJS.fn.checkAnimFrame);\n    cancelRequestAnimFrame(pJS.fn.drawAnimFrame);\n    pJS.tmp.source_svg = undefined;\n    pJS.tmp.img_obj = undefined;\n    pJS.tmp.count_svg = 0;\n    pJS.fn.particlesEmpty();\n    pJS.fn.canvasClear();\n    \n    /* restart */\n    pJS.fn.vendors.start();\n\n  };\n\n\n  /* ---------- pJS functions - particles interaction ------------ */\n\n  pJS.fn.interact.linkParticles = function(p1, p2){\n\n    var dx = p1.x - p2.x,\n        dy = p1.y - p2.y,\n        dist = Math.sqrt(dx*dx + dy*dy);\n\n    /* draw a line between p1 and p2 if the distance between them is under the config distance */\n    if(dist <= pJS.particles.line_linked.distance){\n\n      var opacity_line = pJS.particles.line_linked.opacity - (dist / (1/pJS.particles.line_linked.opacity)) / pJS.particles.line_linked.distance;\n\n      if(opacity_line > 0){        \n        \n        /* style */\n        var color_line = pJS.particles.line_linked.color_rgb_line;\n        pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')';\n        pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width;\n        //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */\n        \n        /* path */\n        pJS.canvas.ctx.beginPath();\n        pJS.canvas.ctx.moveTo(p1.x, p1.y);\n        pJS.canvas.ctx.lineTo(p2.x, p2.y);\n        pJS.canvas.ctx.stroke();\n        pJS.canvas.ctx.closePath();\n\n      }\n\n    }\n\n  };\n\n\n  pJS.fn.interact.attractParticles  = function(p1, p2){\n\n    /* condensed particles */\n    var dx = p1.x - p2.x,\n        dy = p1.y - p2.y,\n        dist = Math.sqrt(dx*dx + dy*dy);\n\n    if(dist <= pJS.particles.line_linked.distance){\n\n      var ax = dx/(pJS.particles.move.attract.rotateX*1000),\n          ay = dy/(pJS.particles.move.attract.rotateY*1000);\n\n      p1.vx -= ax;\n      p1.vy -= ay;\n\n      p2.vx += ax;\n      p2.vy += ay;\n\n    }\n    \n\n  }\n\n\n  pJS.fn.interact.bounceParticles = function(p1, p2){\n\n    var dx = p1.x - p2.x,\n        dy = p1.y - p2.y,\n        dist = Math.sqrt(dx*dx + dy*dy),\n        dist_p = p1.radius+p2.radius;\n\n    if(dist <= dist_p){\n      p1.vx = -p1.vx;\n      p1.vy = -p1.vy;\n\n      p2.vx = -p2.vx;\n      p2.vy = -p2.vy;\n    }\n\n  }\n\n\n  /* ---------- pJS functions - modes events ------------ */\n\n  pJS.fn.modes.pushParticles = function(nb, pos){\n\n    pJS.tmp.pushing = true;\n\n    for(var i = 0; i < nb; i++){\n      pJS.particles.array.push(\n        new pJS.fn.particle(\n          pJS.particles.color,\n          pJS.particles.opacity.value,\n          {\n            'x': pos ? pos.pos_x : Math.random() * pJS.canvas.w,\n            'y': pos ? pos.pos_y : Math.random() * pJS.canvas.h\n          }\n        )\n      )\n      if(i == nb-1){\n        if(!pJS.particles.move.enable){\n          pJS.fn.particlesDraw();\n        }\n        pJS.tmp.pushing = false;\n      }\n    }\n\n  };\n\n\n  pJS.fn.modes.removeParticles = function(nb){\n\n    pJS.particles.array.splice(0, nb);\n    if(!pJS.particles.move.enable){\n      pJS.fn.particlesDraw();\n    }\n\n  };\n\n\n  pJS.fn.modes.bubbleParticle = function(p){\n\n    /* on hover event */\n    if(pJS.interactivity.events.onhover.enable && isInArray('bubble', pJS.interactivity.events.onhover.mode)){\n\n      var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,\n          dy_mouse = p.y - pJS.interactivity.mouse.pos_y,\n          dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse),\n          ratio = 1 - dist_mouse / pJS.interactivity.modes.bubble.distance;\n\n      function init(){\n        p.opacity_bubble = p.opacity;\n        p.radius_bubble = p.radius;\n      }\n\n      /* mousemove - check ratio */\n      if(dist_mouse <= pJS.interactivity.modes.bubble.distance){\n\n        if(ratio >= 0 && pJS.interactivity.status == 'mousemove'){\n          \n          /* size */\n          if(pJS.interactivity.modes.bubble.size != pJS.particles.size.value){\n\n            if(pJS.interactivity.modes.bubble.size > pJS.particles.size.value){\n              var size = p.radius + (pJS.interactivity.modes.bubble.size*ratio);\n              if(size >= 0){\n                p.radius_bubble = size;\n              }\n            }else{\n              var dif = p.radius - pJS.interactivity.modes.bubble.size,\n                  size = p.radius - (dif*ratio);\n              if(size > 0){\n                p.radius_bubble = size;\n              }else{\n                p.radius_bubble = 0;\n              }\n            }\n\n          }\n\n          /* opacity */\n          if(pJS.interactivity.modes.bubble.opacity != pJS.particles.opacity.value){\n\n            if(pJS.interactivity.modes.bubble.opacity > pJS.particles.opacity.value){\n              var opacity = pJS.interactivity.modes.bubble.opacity*ratio;\n              if(opacity > p.opacity && opacity <= pJS.interactivity.modes.bubble.opacity){\n                p.opacity_bubble = opacity;\n              }\n            }else{\n              var opacity = p.opacity - (pJS.particles.opacity.value-pJS.interactivity.modes.bubble.opacity)*ratio;\n              if(opacity < p.opacity && opacity >= pJS.interactivity.modes.bubble.opacity){\n                p.opacity_bubble = opacity;\n              }\n            }\n\n          }\n\n        }\n\n      }else{\n        init();\n      }\n\n\n      /* mouseleave */\n      if(pJS.interactivity.status == 'mouseleave'){\n        init();\n      }\n    \n    }\n\n    /* on click event */\n    else if(pJS.interactivity.events.onclick.enable && isInArray('bubble', pJS.interactivity.events.onclick.mode)){\n\n\n      if(pJS.tmp.bubble_clicking){\n        var dx_mouse = p.x - pJS.interactivity.mouse.click_pos_x,\n            dy_mouse = p.y - pJS.interactivity.mouse.click_pos_y,\n            dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse),\n            time_spent = (new Date().getTime() - pJS.interactivity.mouse.click_time)/1000;\n\n        if(time_spent > pJS.interactivity.modes.bubble.duration){\n          pJS.tmp.bubble_duration_end = true;\n        }\n\n        if(time_spent > pJS.interactivity.modes.bubble.duration*2){\n          pJS.tmp.bubble_clicking = false;\n          pJS.tmp.bubble_duration_end = false;\n        }\n      }\n\n\n      function process(bubble_param, particles_param, p_obj_bubble, p_obj, id){\n\n        if(bubble_param != particles_param){\n\n          if(!pJS.tmp.bubble_duration_end){\n            if(dist_mouse <= pJS.interactivity.modes.bubble.distance){\n              if(p_obj_bubble != undefined) var obj = p_obj_bubble;\n              else var obj = p_obj;\n              if(obj != bubble_param){\n                var value = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration);\n                if(id == 'size') p.radius_bubble = value;\n                if(id == 'opacity') p.opacity_bubble = value;\n              }\n            }else{\n              if(id == 'size') p.radius_bubble = undefined;\n              if(id == 'opacity') p.opacity_bubble = undefined;\n            }\n          }else{\n            if(p_obj_bubble != undefined){\n              var value_tmp = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration),\n                  dif = bubble_param - value_tmp;\n                  value = bubble_param + dif;\n              if(id == 'size') p.radius_bubble = value;\n              if(id == 'opacity') p.opacity_bubble = value;\n            }\n          }\n\n        }\n\n      }\n\n      if(pJS.tmp.bubble_clicking){\n        /* size */\n        process(pJS.interactivity.modes.bubble.size, pJS.particles.size.value, p.radius_bubble, p.radius, 'size');\n        /* opacity */\n        process(pJS.interactivity.modes.bubble.opacity, pJS.particles.opacity.value, p.opacity_bubble, p.opacity, 'opacity');\n      }\n\n    }\n\n  };\n\n\n  pJS.fn.modes.repulseParticle = function(p){\n\n    if(pJS.interactivity.events.onhover.enable && isInArray('repulse', pJS.interactivity.events.onhover.mode) && pJS.interactivity.status == 'mousemove') {\n\n      var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,\n          dy_mouse = p.y - pJS.interactivity.mouse.pos_y,\n          dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse);\n\n      var normVec = {x: dx_mouse/dist_mouse, y: dy_mouse/dist_mouse},\n          repulseRadius = pJS.interactivity.modes.repulse.distance,\n          velocity = 100,\n          repulseFactor = clamp((1/repulseRadius)*(-1*Math.pow(dist_mouse/repulseRadius,2)+1)*repulseRadius*velocity, 0, 50);\n      \n      var pos = {\n        x: p.x + normVec.x * repulseFactor,\n        y: p.y + normVec.y * repulseFactor\n      }\n\n      if(pJS.particles.move.out_mode == 'bounce'){\n        if(pos.x - p.radius > 0 && pos.x + p.radius < pJS.canvas.w) p.x = pos.x;\n        if(pos.y - p.radius > 0 && pos.y + p.radius < pJS.canvas.h) p.y = pos.y;\n      }else{\n        p.x = pos.x;\n        p.y = pos.y;\n      }\n    \n    }\n\n\n    else if(pJS.interactivity.events.onclick.enable && isInArray('repulse', pJS.interactivity.events.onclick.mode)) {\n\n      if(!pJS.tmp.repulse_finish){\n        pJS.tmp.repulse_count++;\n        if(pJS.tmp.repulse_count == pJS.particles.array.length){\n          pJS.tmp.repulse_finish = true;\n        }\n      }\n\n      if(pJS.tmp.repulse_clicking){\n\n        var repulseRadius = Math.pow(pJS.interactivity.modes.repulse.distance/6, 3);\n\n        var dx = pJS.interactivity.mouse.click_pos_x - p.x,\n            dy = pJS.interactivity.mouse.click_pos_y - p.y,\n            d = dx*dx + dy*dy;\n\n        var force = -repulseRadius / d * 1;\n\n        function process(){\n\n          var f = Math.atan2(dy,dx);\n          p.vx = force * Math.cos(f);\n          p.vy = force * Math.sin(f);\n\n          if(pJS.particles.move.out_mode == 'bounce'){\n            var pos = {\n              x: p.x + p.vx,\n              y: p.y + p.vy\n            }\n            if (pos.x + p.radius > pJS.canvas.w) p.vx = -p.vx;\n            else if (pos.x - p.radius < 0) p.vx = -p.vx;\n            if (pos.y + p.radius > pJS.canvas.h) p.vy = -p.vy;\n            else if (pos.y - p.radius < 0) p.vy = -p.vy;\n          }\n\n        }\n\n        // default\n        if(d <= repulseRadius){\n          process();\n        }\n\n        // bang - slow motion mode\n        // if(!pJS.tmp.repulse_finish){\n        //   if(d <= repulseRadius){\n        //     process();\n        //   }\n        // }else{\n        //   process();\n        // }\n        \n\n      }else{\n\n        if(pJS.tmp.repulse_clicking == false){\n\n          p.vx = p.vx_i;\n          p.vy = p.vy_i;\n        \n        }\n\n      }\n\n    }\n\n  }\n\n\n  pJS.fn.modes.grabParticle = function(p){\n\n    if(pJS.interactivity.events.onhover.enable && pJS.interactivity.status == 'mousemove'){\n\n      var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,\n          dy_mouse = p.y - pJS.interactivity.mouse.pos_y,\n          dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse);\n\n      /* draw a line between the cursor and the particle if the distance between them is under the config distance */\n      if(dist_mouse <= pJS.interactivity.modes.grab.distance){\n\n        var opacity_line = pJS.interactivity.modes.grab.line_linked.opacity - (dist_mouse / (1/pJS.interactivity.modes.grab.line_linked.opacity)) / pJS.interactivity.modes.grab.distance;\n\n        if(opacity_line > 0){\n\n          /* style */\n          var color_line = pJS.particles.line_linked.color_rgb_line;\n          pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')';\n          pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width;\n          //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */\n          \n          /* path */\n          pJS.canvas.ctx.beginPath();\n          pJS.canvas.ctx.moveTo(p.x, p.y);\n          pJS.canvas.ctx.lineTo(pJS.interactivity.mouse.pos_x, pJS.interactivity.mouse.pos_y);\n          pJS.canvas.ctx.stroke();\n          pJS.canvas.ctx.closePath();\n\n        }\n\n      }\n\n    }\n\n  };\n\n\n\n  /* ---------- pJS functions - vendors ------------ */\n\n  pJS.fn.vendors.eventsListeners = function(){\n\n    /* events target element */\n    if(pJS.interactivity.detect_on == 'window'){\n      pJS.interactivity.el = window;\n    }else{\n      pJS.interactivity.el = pJS.canvas.el;\n    }\n\n\n    /* detect mouse pos - on hover / click event */\n    if(pJS.interactivity.events.onhover.enable || pJS.interactivity.events.onclick.enable){\n\n      /* el on mousemove */\n      pJS.interactivity.el.addEventListener('mousemove', function(e){\n\n        if(pJS.interactivity.el == window){\n          var pos_x = e.clientX,\n              pos_y = e.clientY;\n        }\n        else{\n          var pos_x = e.offsetX || e.clientX,\n              pos_y = e.offsetY || e.clientY;\n        }\n\n        pJS.interactivity.mouse.pos_x = pos_x;\n        pJS.interactivity.mouse.pos_y = pos_y;\n\n        if(pJS.tmp.retina){\n          pJS.interactivity.mouse.pos_x *= pJS.canvas.pxratio;\n          pJS.interactivity.mouse.pos_y *= pJS.canvas.pxratio;\n        }\n\n        pJS.interactivity.status = 'mousemove';\n\n      });\n\n      /* el on onmouseleave */\n      pJS.interactivity.el.addEventListener('mouseleave', function(e){\n\n        pJS.interactivity.mouse.pos_x = null;\n        pJS.interactivity.mouse.pos_y = null;\n        pJS.interactivity.status = 'mouseleave';\n\n      });\n\n    }\n\n    /* on click event */\n    if(pJS.interactivity.events.onclick.enable){\n\n      pJS.interactivity.el.addEventListener('click', function(){\n\n        pJS.interactivity.mouse.click_pos_x = pJS.interactivity.mouse.pos_x;\n        pJS.interactivity.mouse.click_pos_y = pJS.interactivity.mouse.pos_y;\n        pJS.interactivity.mouse.click_time = new Date().getTime();\n\n        if(pJS.interactivity.events.onclick.enable){\n\n          switch(pJS.interactivity.events.onclick.mode){\n\n            case 'push':\n              if(pJS.particles.move.enable){\n                pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse);\n              }else{\n                if(pJS.interactivity.modes.push.particles_nb == 1){\n                  pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse);\n                }\n                else if(pJS.interactivity.modes.push.particles_nb > 1){\n                  pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb);\n                }\n              }\n            break;\n\n            case 'remove':\n              pJS.fn.modes.removeParticles(pJS.interactivity.modes.remove.particles_nb);\n            break;\n\n            case 'bubble':\n              pJS.tmp.bubble_clicking = true;\n            break;\n\n            case 'repulse':\n              pJS.tmp.repulse_clicking = true;\n              pJS.tmp.repulse_count = 0;\n              pJS.tmp.repulse_finish = false;\n              setTimeout(function(){\n                pJS.tmp.repulse_clicking = false;\n              }, pJS.interactivity.modes.repulse.duration*1000)\n            break;\n\n          }\n\n        }\n\n      });\n        \n    }\n\n\n  };\n\n  pJS.fn.vendors.densityAutoParticles = function(){\n\n    if(pJS.particles.number.density.enable){\n\n      /* calc area */\n      var area = pJS.canvas.el.width * pJS.canvas.el.height / 1000;\n      if(pJS.tmp.retina){\n        area = area/(pJS.canvas.pxratio*2);\n      }\n\n      /* calc number of particles based on density area */\n      var nb_particles = area * pJS.particles.number.value / pJS.particles.number.density.value_area;\n\n      /* add or remove X particles */\n      var missing_particles = pJS.particles.array.length - nb_particles;\n      if(missing_particles < 0) pJS.fn.modes.pushParticles(Math.abs(missing_particles));\n      else pJS.fn.modes.removeParticles(missing_particles);\n\n    }\n\n  };\n\n\n  pJS.fn.vendors.checkOverlap = function(p1, position){\n    for(var i = 0; i < pJS.particles.array.length; i++){\n      var p2 = pJS.particles.array[i];\n\n      var dx = p1.x - p2.x,\n          dy = p1.y - p2.y,\n          dist = Math.sqrt(dx*dx + dy*dy);\n\n      if(dist <= p1.radius + p2.radius){\n        p1.x = position ? position.x : Math.random() * pJS.canvas.w;\n        p1.y = position ? position.y : Math.random() * pJS.canvas.h;\n        pJS.fn.vendors.checkOverlap(p1);\n      }\n    }\n  };\n\n\n  pJS.fn.vendors.createSvgImg = function(p){\n\n    /* set color to svg element */\n    var svgXml = pJS.tmp.source_svg,\n        rgbHex = /#([0-9A-F]{3,6})/gi,\n        coloredSvgXml = svgXml.replace(rgbHex, function (m, r, g, b) {\n          if(p.color.rgb){\n            var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+p.opacity+')';\n          }else{\n            var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+p.opacity+')';\n          }\n          return color_value;\n        });\n\n    /* prepare to create img with colored svg */\n    var svg = new Blob([coloredSvgXml], {type: 'image/svg+xml;charset=utf-8'}),\n        DOMURL = window.URL || window.webkitURL || window,\n        url = DOMURL.createObjectURL(svg);\n\n    /* create particle img obj */\n    var img = new Image();\n    img.addEventListener('load', function(){\n      p.img.obj = img;\n      p.img.loaded = true;\n      DOMURL.revokeObjectURL(url);\n      pJS.tmp.count_svg++;\n    });\n    img.src = url;\n\n  };\n\n\n  pJS.fn.vendors.destroypJS = function(){\n    cancelAnimationFrame(pJS.fn.drawAnimFrame);\n    canvas_el.remove();\n    pJSDom = null;\n  };\n\n\n  pJS.fn.vendors.drawShape = function(c, startX, startY, sideLength, sideCountNumerator, sideCountDenominator){\n\n    // By Programming Thomas - https://programmingthomas.wordpress.com/2013/04/03/n-sided-shapes/\n    var sideCount = sideCountNumerator * sideCountDenominator;\n    var decimalSides = sideCountNumerator / sideCountDenominator;\n    var interiorAngleDegrees = (180 * (decimalSides - 2)) / decimalSides;\n    var interiorAngle = Math.PI - Math.PI * interiorAngleDegrees / 180; // convert to radians\n    c.save();\n    c.beginPath();\n    c.translate(startX, startY);\n    c.moveTo(0,0);\n    for (var i = 0; i < sideCount; i++) {\n      c.lineTo(sideLength,0);\n      c.translate(sideLength,0);\n      c.rotate(interiorAngle);\n    }\n    //c.stroke();\n    c.fill();\n    c.restore();\n\n  };\n\n  pJS.fn.vendors.exportImg = function(){\n    window.open(pJS.canvas.el.toDataURL('image/png'), '_blank');\n  };\n\n\n  pJS.fn.vendors.loadImg = function(type){\n\n    pJS.tmp.img_error = undefined;\n\n    if(pJS.particles.shape.image.src != ''){\n\n      if(type == 'svg'){\n\n        var xhr = new XMLHttpRequest();\n        xhr.open('GET', pJS.particles.shape.image.src);\n        xhr.onreadystatechange = function (data) {\n          if(xhr.readyState == 4){\n            if(xhr.status == 200){\n              pJS.tmp.source_svg = data.currentTarget.response;\n              pJS.fn.vendors.checkBeforeDraw();\n            }else{\n              console.log('Error pJS - Image not found');\n              pJS.tmp.img_error = true;\n            }\n          }\n        }\n        xhr.send();\n\n      }else{\n\n        var img = new Image();\n        img.addEventListener('load', function(){\n          pJS.tmp.img_obj = img;\n          pJS.fn.vendors.checkBeforeDraw();\n        });\n        img.src = pJS.particles.shape.image.src;\n\n      }\n\n    }else{\n      console.log('Error pJS - No image.src');\n      pJS.tmp.img_error = true;\n    }\n\n  };\n\n\n  pJS.fn.vendors.draw = function(){\n\n    if(pJS.particles.shape.type == 'image'){\n\n      if(pJS.tmp.img_type == 'svg'){\n\n        if(pJS.tmp.count_svg >= pJS.particles.number.value){\n          pJS.fn.particlesDraw();\n          if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);\n          else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n        }else{\n          //console.log('still loading...');\n          if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n        }\n\n      }else{\n\n        if(pJS.tmp.img_obj != undefined){\n          pJS.fn.particlesDraw();\n          if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);\n          else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n        }else{\n          if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n        }\n\n      }\n\n    }else{\n      pJS.fn.particlesDraw();\n      if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);\n      else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n    }\n\n  };\n\n\n  pJS.fn.vendors.checkBeforeDraw = function(){\n\n    // if shape is image\n    if(pJS.particles.shape.type == 'image'){\n\n      if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg == undefined){\n        pJS.tmp.checkAnimFrame = requestAnimFrame(check);\n      }else{\n        //console.log('images loaded! cancel check');\n        cancelRequestAnimFrame(pJS.tmp.checkAnimFrame);\n        if(!pJS.tmp.img_error){\n          pJS.fn.vendors.init();\n          pJS.fn.vendors.draw();\n        }\n        \n      }\n\n    }else{\n      pJS.fn.vendors.init();\n      pJS.fn.vendors.draw();\n    }\n\n  };\n\n\n  pJS.fn.vendors.init = function(){\n\n    /* init canvas + particles */\n    pJS.fn.retinaInit();\n    pJS.fn.canvasInit();\n    pJS.fn.canvasSize();\n    pJS.fn.canvasPaint();\n    pJS.fn.particlesCreate();\n    pJS.fn.vendors.densityAutoParticles();\n\n    /* particles.line_linked - convert hex colors to rgb */\n    pJS.particles.line_linked.color_rgb_line = hexToRgb(pJS.particles.line_linked.color);\n\n  };\n\n\n  pJS.fn.vendors.start = function(){\n\n    if(isInArray('image', pJS.particles.shape.type)){\n      pJS.tmp.img_type = pJS.particles.shape.image.src.substr(pJS.particles.shape.image.src.length - 3);\n      pJS.fn.vendors.loadImg(pJS.tmp.img_type);\n    }else{\n      pJS.fn.vendors.checkBeforeDraw();\n    }\n\n  };\n\n\n\n\n  /* ---------- pJS - start ------------ */\n\n\n  pJS.fn.vendors.eventsListeners();\n\n  pJS.fn.vendors.start();\n  \n\n\n};\n\n/* ---------- global functions - vendors ------------ */\n\nObject.deepExtend = function(destination, source) {\n  for (var property in source) {\n    if (source[property] && source[property].constructor &&\n     source[property].constructor === Object) {\n      destination[property] = destination[property] || {};\n      arguments.callee(destination[property], source[property]);\n    } else {\n      destination[property] = source[property];\n    }\n  }\n  return destination;\n};\n\nwindow.requestAnimFrame = (function(){\n  return  window.requestAnimationFrame ||\n    window.webkitRequestAnimationFrame ||\n    window.mozRequestAnimationFrame    ||\n    window.oRequestAnimationFrame      ||\n    window.msRequestAnimationFrame     ||\n    function(callback){\n      window.setTimeout(callback, 1000 / 60);\n    };\n})();\n\nwindow.cancelRequestAnimFrame = ( function() {\n  return window.cancelAnimationFrame         ||\n    window.webkitCancelRequestAnimationFrame ||\n    window.mozCancelRequestAnimationFrame    ||\n    window.oCancelRequestAnimationFrame      ||\n    window.msCancelRequestAnimationFrame     ||\n    clearTimeout\n} )();\n\nfunction hexToRgb(hex){\n  // By Tim Down - http://stackoverflow.com/a/5624139/3493650\n  // Expand shorthand form (e.g. \"03F\") to full form (e.g. \"0033FF\")\n  var shorthandRegex = /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i;\n  hex = hex.replace(shorthandRegex, function(m, r, g, b) {\n     return r + r + g + g + b + b;\n  });\n  var result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n  return result ? {\n      r: parseInt(result[1], 16),\n      g: parseInt(result[2], 16),\n      b: parseInt(result[3], 16)\n  } : null;\n};\n\nfunction clamp(number, min, max) {\n  return Math.min(Math.max(number, min), max);\n};\n\nfunction isInArray(value, array) {\n  return array.indexOf(value) > -1;\n}\n\n\n/* ---------- particles.js functions - start ------------ */\n\nwindow.pJSDom = [];\n\nwindow.particlesJS = function(tag_id, params){\n\n  //console.log(params);\n\n  /* no string id? so it's object params, and set the id with default id */\n  if(typeof(tag_id) != 'string'){\n    params = tag_id;\n    tag_id = 'particles-js';\n  }\n\n  /* no id? set the id to default id */\n  if(!tag_id){\n    tag_id = 'particles-js';\n  }\n\n  /* pJS elements */\n  var pJS_tag = document.getElementById(tag_id),\n      pJS_canvas_class = 'particles-js-canvas-el',\n      exist_canvas = pJS_tag.getElementsByClassName(pJS_canvas_class);\n\n  /* remove canvas if exists into the pJS target tag */\n  if(exist_canvas.length){\n    while(exist_canvas.length > 0){\n      pJS_tag.removeChild(exist_canvas[0]);\n    }\n  }\n\n  /* create canvas element */\n  var canvas_el = document.createElement('canvas');\n  canvas_el.className = pJS_canvas_class;\n\n  /* set size canvas */\n  canvas_el.style.width = \"100%\";\n  canvas_el.style.height = \"100%\";\n\n  /* append canvas */\n  var canvas = document.getElementById(tag_id).appendChild(canvas_el);\n\n  /* launch particle.js */\n  if(canvas != null){\n    pJSDom.push(new pJS(tag_id, params));\n  }\n\n};\n\nwindow.particlesJS.load = function(tag_id, path_config_json, callback){\n\n  /* load json config */\n  var xhr = new XMLHttpRequest();\n  xhr.open('GET', path_config_json);\n  xhr.onreadystatechange = function (data) {\n    if(xhr.readyState == 4){\n      if(xhr.status == 200){\n        var params = JSON.parse(data.currentTarget.response);\n        window.particlesJS(tag_id, params);\n        if(callback) callback();\n      }else{\n        console.log('Error pJS - XMLHttpRequest status: '+xhr.status);\n        console.log('Error pJS - File config not found');\n      }\n    }\n  };\n  xhr.send();\n\n};"
  }
]