diff --git a/.travis.yml b/.travis.yml
index 6e25621b..afc82b45 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,13 +6,23 @@ python:
- 3.4
- 3.5
- pypy
-cache: pip
+cache:
+ - pip
+ - directories:
+ - .cache/pip/
+ - docs/node_modules
install:
-- pip install --cache-dir $HOME/.cache/pip pytest pytest-cov coveralls flake8 six blinker pytest-django
-- pip install --cache-dir $HOME/.cache/pip -e .[django]
+- pip install --download-cache .cache/pip/ pytest pytest-cov coveralls flake8 six
+ blinker pytest-django
+- pip install --download-cache .cache/pip/ -e .[django]
- python setup.py develop
script:
- py.test --cov=graphene
- flake8
after_success:
- coveralls
+- cd docs && npm run deploy
+
+env:
+ matrix:
+ secure: e0iu1FvUcGNcsKEDsGQiGTJZMJ8tJMAQ+c+KCuh7I11likoJbninI9FX85efMglG93Xu4GtND1rUwKWnANM94Cz5IrLrT9ga62Cg1sinbzD+JUOG9EobtmcYcLbfNvA1ybkZf4K0KvkYfH1XUxtL3v6jFI6lLjZ1vKa2M6i5t/38pjgUOOh9KlVGi+rqSp2TUsCyjZkMxE2Wwceen5N/B1yZJDn9yHPA+kiV2ScOMgZOuVMqQd0IAPcjhqTbNdaW2VjNQFtS8CQ8FOjJh9NepRAyA42oII7Aq65YzZbifzYL1eSPKKfUHHdc7Bs+vq1kGuaWz+XBqByZAQvw7OtpkecG+KrcO7XDV38y6z33vgCC5MPu9e0BZwITIvyuUOKmFA9vTAx/w963vBDSzHgskgqYtYswxzOoE55TS9tHsc5rgoSatEGW2VXyI8ytWpgkuluCNwT+/ZvNaQ33SCPTwN7mQGWx+DC+eyMBjI1sP9s4aYCaTggCSFNtwhRpQqqM/0HFa7hHDksK/zJhl4fhFUrbmyrJo6wm6Z7/s/WiSulk+zZkrLC9eBs1+XNtU7PzcmgfFmcsJnPGBmBF8WOa8WiX9hOn9DutBT4mLtlOod3YvU22U0Vwj2TjzhDvx8uyYCpDA03a/q9QjE4+klcI2Mw5UNhJsmTpCZQE06aHDKg=
diff --git a/docs/.babelrc b/docs/.babelrc
new file mode 100644
index 00000000..ce840ab8
--- /dev/null
+++ b/docs/.babelrc
@@ -0,0 +1,3 @@
+{
+ "stage": 0
+}
\ No newline at end of file
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000..548fd2f1
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,29 @@
+# Logs
+logs
+*.log
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directory
+# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
+node_modules
+
+public/
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 00000000..2a8f240f
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,16 @@
+# Graphene Docs
+Docs for Graphene
+
+
+## Installation
+
+For running locally this docs. You have to execute
+```bash
+npm install -g gatsby && npm install
+```
+
+And then
+
+```bash
+gatsby develop
+```
\ No newline at end of file
diff --git a/docs/app.js b/docs/app.js
new file mode 100644
index 00000000..148e5fd5
--- /dev/null
+++ b/docs/app.js
@@ -0,0 +1,11 @@
+exports.loadContext = function(callback) {
+ var context;
+ context = require.context('./pages', true);
+ if (module.hot) {
+ module.hot.accept(context.id, function() {
+ context = require.context('./pages', true);
+ return callback(context);
+ });
+ }
+ return callback(context);
+};
diff --git a/docs/assets/icon.js b/docs/assets/icon.js
new file mode 100644
index 00000000..1d0b3c61
--- /dev/null
+++ b/docs/assets/icon.js
@@ -0,0 +1,7 @@
+import React from 'react';
+
+export default class Icon extends React.Component {
+ render() {
+ return
+ }
+}
diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg
new file mode 100644
index 00000000..297830fe
--- /dev/null
+++ b/docs/assets/logo.svg
@@ -0,0 +1,10 @@
+
diff --git a/docs/assets/starwars-icon.png b/docs/assets/starwars-icon.png
new file mode 100644
index 00000000..b99a2a0a
Binary files /dev/null and b/docs/assets/starwars-icon.png differ
diff --git a/docs/assets/starwars-icon@2x.png b/docs/assets/starwars-icon@2x.png
new file mode 100644
index 00000000..7f2239c3
Binary files /dev/null and b/docs/assets/starwars-icon@2x.png differ
diff --git a/docs/config.toml b/docs/config.toml
new file mode 100644
index 00000000..08df109a
--- /dev/null
+++ b/docs/config.toml
@@ -0,0 +1 @@
+siteTitle="Graphene"
diff --git a/docs/css/main.styl b/docs/css/main.styl
new file mode 100644
index 00000000..71c00739
--- /dev/null
+++ b/docs/css/main.styl
@@ -0,0 +1,212 @@
+@import 'nib'
+@import 'jeet'
+
+@import 'https://fonts.googleapis.com/css?family=Raleway:400,500,600,200,100&.css'
+
+normalize-css()
+
+$wrapper
+ center(960px, pad:20px)
+
+.wrapper
+ @extend $wrapper
+ position relative
+
+a, a:hover
+ text-decoration none
+
+html, body
+ font-family "Helvetica Neue", Helvetica, Arial, sans-serif
+ font-weight 300
+ font-size 16px
+ color #606060
+ line-height 1.5
+
+.header
+ clearfix()
+ position relative
+ text-align center
+ background: #DB594C;
+ background-image: radial-gradient(95% 101%, #E46643 5%, rgba(226,91,72,0.00) 100%)
+ .logo
+ width 42px
+ height @width
+ vertical-align middle
+
+ h1
+ max-width 380px
+ font-family 'Raleway', sans-serif
+ font-weight 200
+ font-size 42px
+ color #FFFFFF
+ line-height 49px
+ margin 80px auto 40px
+ z-index 110
+
+ .get-started
+ font-family 'Raleway'
+ display inline-block
+ margin 0 auto
+ font-size 13px
+ color #FFFFFF
+ padding 0 18px
+ text-transform uppercase
+ font-weight 600
+ line-height 15px
+ border 1px solid #FFFFFF
+ border-radius 2px
+ padding 12px 18px
+ z-index 111
+ position relative
+ &:hover
+ background white
+ color #E05B49
+ text-decoration none
+
+.header-wrapper
+ @extend $wrapper
+ text-align left
+ padding-top 32px
+ padding-bottom 32px
+ position relative
+ z-index 100
+
+.header-extended
+ padding-bottom 100px
+
+.header-nav
+ margin-top 8px
+ a
+ font-family 'Raleway'
+ font-size 13px
+ color #FFFFFF
+ margin 0 16px
+ padding 0 2px
+ text-transform uppercase
+ font-weight 600
+ line-height 15px
+
+.header-logo
+ font-family 'Raleway'
+ font-size 22px
+ color #FFFFFF
+ float left
+ font-weight 500
+ text-transform uppercase
+ text-decoration none
+
+.header-nav
+ float right
+
+.logo
+ path
+ stroke-dasharray 250
+ stroke-dashoffset 250
+ animation logo-dash .9s ease-in-out forwards
+ animation-delay .12s
+ g
+ ellipse
+ animation logo-dot .3s ease forwards
+ animation-fill-mode both
+ transform-origin 50% 50%
+ &:nth-child(2)
+ ellipse
+ animation-delay .1s
+ &:nth-child(3)
+ ellipse
+ animation-delay .2s
+ &:nth-child(4)
+ ellipse
+ animation-delay .3s
+ &:nth-child(5)
+ ellipse
+ animation-delay .4s
+ &:nth-child(6)
+ ellipse
+ animation-delay .5s
+ &:nth-child(7)
+ ellipse
+ animation-delay .6s
+ &:nth-child(8)
+ ellipse
+ animation-delay .7s
+
+@keyframes logo-dash
+ to
+ stroke-dashoffset 0
+
+@keyframes logo-dot
+ from
+ opacity 0.5
+ transform scale(0)
+ to
+ opacity 1
+ transform scale(1)
+
+#header-background
+ z-index 0
+ display block
+ position absolute
+ width 100%
+ top 0
+ bottom 0
+ right 0
+ left 0
+
+.particles-js-canvas-el
+ display block
+ opacity 0
+ position absolute
+
+.starwars-example
+ background: #3C3C3C
+ display inline-block
+ position absolute
+ right 20px
+ top -100px
+ box-shadow: 0px 2px 5px 0px rgba(0,0,0,0.25);
+ border-radius: 100px
+ font-size 13px
+ padding 17px 17px 17px 71px
+ width 226px
+ box-sizing border-box
+ color white
+ font-family 'Raleway'
+ font-weight 500
+ &:before
+ content: ''
+ display block
+ position absolute
+ left 0
+ top 0
+ height 32px
+ width 32px
+ image '../assets/starwars-icon.png'
+
+.improve-document-link
+ @extend $wrapper
+
+.markdown
+ .wrapper
+ margin-top 60px
+
+ h1, h2, h3, h4, h5, h6
+ font-family: 'Raleway';
+ font-weight 600
+ color #4A4A4A
+ strong
+ font-weight 500
+
+ margin-bottom 40px
+
+.title
+ background: #F9F9F9;
+ padding 30px 0
+
+ h1
+ @extend $wrapper
+ font-family: 'Raleway';
+ font-size: 42px;
+ font-weight 200
+ color: #585858;
+ line-height: 49px;
diff --git a/docs/gatsby.config.js b/docs/gatsby.config.js
new file mode 100644
index 00000000..80be7b41
--- /dev/null
+++ b/docs/gatsby.config.js
@@ -0,0 +1,26 @@
+var nib = require("nib");
+var jeet = require("jeet");
+var ExtractTextPlugin = require("extract-text-webpack-plugin");
+
+module.exports = function(config, env) {
+ var IS_STATIC = env === 'static';
+ config.merge({
+ stylus: {
+ use: [nib(), jeet()]
+ }
+ });
+ if (IS_STATIC) {
+ config.plugin('extract-css', ExtractTextPlugin, ["app.css"]);
+ }
+ config.loader('stylus', function(cfg) {
+ cfg.test = /\.styl$/;
+ if (IS_STATIC) {
+ cfg.loader = ExtractTextPlugin.extract('style-loader', 'css-loader!stylus-loader', { allChunks: true });
+ }
+ else {
+ cfg.loader = 'style-loader!css-loader!stylus-loader';
+ }
+ return cfg
+ });
+ return config;
+};
diff --git a/docs/html.js b/docs/html.js
new file mode 100644
index 00000000..e93eda04
--- /dev/null
+++ b/docs/html.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import DocumentTitle from 'react-document-title';
+
+export default class Html extends React.Component {
+ render() {
+ var title = this.props.title || DocumentTitle.rewind();
+ return (
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/docs/package.json b/docs/package.json
new file mode 100644
index 00000000..7ef2b3b9
--- /dev/null
+++ b/docs/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "graphene-docs",
+ "version": "1.0.0",
+ "description": "Graphene docs",
+ "main": "n/a",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "npm install -g gatsby && npm install",
+ "deploy": "npm run build"
+ },
+ "keywords": [
+ "graphene"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "extract-text-webpack-plugin": "^0.9.1",
+ "jeet": "^6.1.2",
+ "nib": "^1.1.0",
+ "react": "^0.14.3",
+ "react-document-title": "^2.0.1",
+ "react-router": "^0.13.5",
+ "stylus-loader": "^1.4.2",
+ "webpack": "^1.12.9"
+ }
+}
diff --git a/docs/pages/_header.js b/docs/pages/_header.js
new file mode 100644
index 00000000..9d70ef03
--- /dev/null
+++ b/docs/pages/_header.js
@@ -0,0 +1,150 @@
+var IN_BROWSER = typeof window != 'undefined';
+import React from 'react';
+if (IN_BROWSER) {
+ var glfx = require('../vendor/glfx.optim')
+ var particlesJS = require('../vendor/particles.js')
+}
+
+class Header extends React.Component {
+ update() {
+ if (!this.mounted) return;
+ var w = this.video.width;
+ var h = this.video.height;
+ if (window.devicePixelRatio > 1) {
+ w = w/2;
+ h = h/2;
+ }
+ this.texture._.initFromCanvas(w, h, this.video);
+
+ this.canvas.draw(this.texture).tiltShift(0, h*2/3, 10, h*2/3, 15, 200).update();
+ requestAnimationFrame(this.update.bind(this));
+ }
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+ componentDidMount() {
+ if (!IN_BROWSER) return;
+ this.mounted = true;
+ new particlesJS('header-background', {
+ "particles": {
+ "number": {
+ "value": 36,
+ "density": {
+ "enable": true,
+ "value_area": 800
+ }
+ },
+ "color": {
+ "value": "#ffffff"
+ },
+ "shape": {
+ "type": "circle",
+ "stroke": {
+ "width": 0,
+ "color": "#000000"
+ },
+ "polygon": {
+ "nb_sides": 8
+ }
+ },
+ "opacity": {
+ "value": 1,
+ "random": true,
+ "anim": {
+ "enable": true,
+ "speed": .1,
+ "opacity_min": .1,
+ "sync": true
+ }
+ },
+ "size": {
+ "value": 2.2,
+ "random": false,
+ "anim": {
+ "enable": false,
+ "speed": .2,
+ "size_min": 2.44,
+ "sync": true
+ }
+ },
+ "line_linked": {
+ "enable": true,
+ "distance": 240,
+ "color": "#ffffff",
+ "opacity": .2,
+ "width": 3
+ },
+ "move": {
+ "enable": true,
+ "speed": 1.1,
+ "direction": "none",
+ "random": true,
+ "straight": false,
+ "out_mode": "bounce",
+ "bounce": false,
+ "attract": {
+ "enable": false,
+ "rotateX": 600,
+ "rotateY": 600
+ }
+ }
+ },
+ "interactivity": {
+ "detect_on": "canvas",
+ "events": {
+ "onhover": {
+ "enable": true,
+ "mode": "repulse"
+ },
+ "onclick": {
+ "enable": true,
+ "mode": "repulse"
+ },
+ "resize": true
+ },
+ "modes": {
+ "grab": {
+ "distance": 400,
+ "line_linked": {
+ "opacity": 1
+ }
+ },
+ "bubble": {
+ "distance": 250,
+ "size": 0,
+ "duration": 2,
+ "opacity": 0,
+ "speed": 3
+ },
+ "repulse": {
+ "distance": 73.08694910712113,
+ "duration": 0.4
+ },
+ "push": {
+ "particles_nb": 4
+ },
+ "remove": {
+ "particles_nb": 2
+ }
+ }
+ },
+ "retina_detect": true
+ });
+ var header_background = document.getElementById('header-background');
+ this.canvas = glfx.canvas();
+ this.video = header_background.children[0];
+ this.context = window.pJSDom[0].pJS.canvas.ctx;
+ this.texture = this.canvas.texture(this.video);
+ header_background.appendChild(this.canvas);
+ requestAnimationFrame(this.update.bind(this));
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+module.exports = Header;
diff --git a/docs/pages/_template.js b/docs/pages/_template.js
new file mode 100644
index 00000000..e077d005
--- /dev/null
+++ b/docs/pages/_template.js
@@ -0,0 +1,44 @@
+import React from 'react';
+import { RouteHandler, Link, State } from 'react-router';
+import Icon from 'assets/icon'
+
+import Header from './_header'
+import logo from '!raw!assets/logo.svg'
+import styles from '../css/main.styl'
+
+class Template extends React.Component {
+ render() {
+ var path = this.props.page.path;
+ var isIndex = path == '/';
+ return (
+
+
+
+
+
+ Graphene
+
+
+
+ {isIndex?
+
+
+ GraphQL in Python
+ made simple
+
+ Get Started
+
+ :null}
+
+
+
+ );
+ }
+}
+
+module.exports = Template;
diff --git a/docs/pages/community.md b/docs/pages/community.md
new file mode 100644
index 00000000..5ad38de1
--- /dev/null
+++ b/docs/pages/community.md
@@ -0,0 +1,25 @@
+---
+layout: page
+title: Community
+active_tab: community
+description: The biggest GraphQL Community in Python
+---
+
+Graphene is constantly developing thanks to an active volunteer community, There are many different places where you discuss Graphene, share your experiences or ask for help. Your feedback and participation are very welcome!
+
+If you think working with Graphene is fun, there are many ways you can contribute to it. Please join us in the Slack community and help us shape the next generation API’s.
+
+[![Public Slack Discussion](https://graphql-slack.herokuapp.com/badge.svg)](https://graphql-slack.herokuapp.com/)
+
+## Our Repositories
+
+- **GraphQL Core**: [Source Code] - [PyPI package]
+- **GraphQL Relay**: [Source Code][1] - [PyPI package][2]
+- **Graphene**: [Source Code][3] - [PyPI package][4]
+
+ [Source Code]: https://github.com/graphql-python/graphql-core
+ [PyPI package]: https://pypi.python.org/pypi/graphql-core
+ [1]: https://github.com/graphql-python/graphql-relay
+ [2]: https://pypi.python.org/pypi/graphql-relay
+ [3]: https://github.com/graphql-python/graphene
+ [4]: https://pypi.python.org/pypi/graphene
diff --git a/docs/pages/docs/django_quickstart.md b/docs/pages/docs/django_quickstart.md
new file mode 100644
index 00000000..e88fbcdf
--- /dev/null
+++ b/docs/pages/docs/django_quickstart.md
@@ -0,0 +1,125 @@
+---
+title: Django Quickstart Guide
+active_tab: quickstart
+description: A Quick guide to Graphene in Django
+---
+
+## Django Quickstart
+
+In our previous quickstart page we created a very simple schema.
+Now we will adapt the schema for our Django models.
+
+## Project setup
+
+```bash
+# Create the project directory
+mkdir tutorial
+cd tutorial
+
+# Create a virtualenv to isolate our package dependencies locally
+virtualenv env
+source env/bin/activate # On Windows use `env\Scripts\activate`
+
+# Install Django and Graphene with Django support
+pip install django
+pip install graphene[django]
+pip install django-graphiql
+
+# Set up a new project with a single application
+django-admin.py startproject tutorial . # Note the trailing '.' character
+cd tutorial
+django-admin.py startapp quickstart
+```
+
+Now sync your database for the first time:
+
+```bash
+python manage.py migrate
+```
+
+We'll also create an initial user named `admin` with a password of `password`.
+
+```bash
+python manage.py createsuperuser
+```
+
+Once you've set up a database and initial user created and ready to go, open up the app's directory and we'll get coding...
+
+
+
+## Schema
+
+Right, we'd better write some types then. Open `tutorial/quickstart/schema.py` and get typing.
+
+```python
+import graphene
+from graphene.contrib.django import DjangoObjectType
+
+from django.contrib.auth.models import User, Group
+
+# Graphene will map automaticall the User model to UserType with
+# the specified fields
+class UserType(DjangoObjectType):
+ class Meta:
+ model = User
+ only_fields = ('username', 'email', 'groups')
+
+
+class GroupType(DjangoObjectType):
+ class Meta:
+ model = User
+ only_fields = ('name', )
+
+
+class Query(graphene.ObjectType):
+ all_users = graphene.List(UserType)
+ get_user = graphene.Field(UserType
+ id=graphene.String(required=True))
+
+ def resolve_all_users(self, args, info):
+ return User.objects.all()
+
+ def resolve_get_user(self, args, info):
+ return User.objects.get(id=args.get('id'))
+
+schema = graphene.Schema(query=Query)
+```
+
+
+## Creating GraphQL and GraphiQL views
+
+Okay, now let's wire up the GraphQL and GraphiQL urls. On to `tutorial/urls.py`...
+
+
+```python
+from django.conf.urls import url, include
+from django.views.decorators.csrf import csrf_exempt
+from graphene.contrib.django.views import GraphQLView
+from tutorial.quickstart.schema import schema
+
+
+# Wire up our GraphQL schema in /graphql.
+# Additionally, we include GraphiQL view for querying easily our schema.
+urlpatterns = [
+ url(r'^graphql', csrf_exempt(GraphQLView.as_view(schema=schema))),
+ url(r'^graphiql', include('django_graphiql.urls')),
+]
+```
+
+## Testing our GraphQL schema
+
+We're now ready to test the API we've built. Let's fire up the server from the command line.
+
+```bash
+python ./manage.py runserver
+```
+
+Go to [localhost:8080/graphiql](http://localhost:8080/graphiql) and type your first query!
+
+```graphql
+myQuery {
+ getUser(id:"1") {
+ username
+ }
+}
+```
diff --git a/docs/pages/docs/quickstart.md b/docs/pages/docs/quickstart.md
new file mode 100644
index 00000000..f408a851
--- /dev/null
+++ b/docs/pages/docs/quickstart.md
@@ -0,0 +1,58 @@
+---
+title: Quickstart Guide
+active_tab: quickstart
+description: A Quick guide to Graphene
+---
+
+Graphene is a powerful framework for creating GraphQL schemas easily in Python.
+
+## Requirements
+
+- Python (2.6.5+, 2.7, 3.2, 3.3, 3.4, 3.5, pypy)
+- Graphene (0.4+)
+
+
+## Project setup
+
+```bash
+# Create the project directory
+mkdir tutorial
+cd tutorial
+
+# Create a virtualenv to isolate our package dependencies locally
+virtualenv env
+source env/bin/activate # On Windows use `env\Scripts\activate`
+
+pip install graphene
+```
+
+## Creating a basic Schema
+
+A GraphQL schema describes your data model, and provides a GraphQL server with an associated set of resolve methods that know how to fetch data.
+
+We are going to create a very simple schema, with a `Query` with only one field: `hello`. And when we query it should return 'World'.
+
+
+```python
+import graphene
+
+class Query(graphene.ObjectType):
+ hello = graphene.String()
+
+ def resolve_hello(self, args, info):
+ return 'World'
+
+schema = graphene.Schema(query=Query)
+```
+
+
+## Querying
+
+Then, we can start querying our schema:
+
+```python
+result = schema.execute('{ hello }')
+print result.data['hello'] # "World"
+```
+
+Congrats! You got your first graphene schema working!
diff --git a/docs/pages/guide/interface.md b/docs/pages/guide/interface.md
new file mode 100644
index 00000000..9f5ff2b9
--- /dev/null
+++ b/docs/pages/guide/interface.md
@@ -0,0 +1,49 @@
+# Interfaces
+
+An Interface contains the essential fields that will be shared among multiple ObjectTypes.
+
+The basics:
+
+- Each Interface is a Python class that inherits from graphene.Interface.
+- Each attribute of the Interface represents a GraphQL field.
+
+## Quick example
+
+This example model defines a Character, which has a name. `Human` and `Droid` inherit from it.
+
+```python
+import graphene
+
+# Character is an Interface
+class Character(graphene.Interface):
+ name = graphene.String()
+
+# Human is an ObjectType, as inherits an interface
+class Human(Character):
+ born_in = graphene.String()
+
+# Droid is an ObjectType, as inherits an interface
+class Droid(Character):
+ function = graphene.String()
+
+```
+
+**name** is a field in the `Character` interface that will be in both `Human` and `Droid` ObjectTypes (as those inherit from Character). Each ObjectType also define extra fields at the same time.
+
+The above types would have the following representation in a schema:
+
+```graphql
+interface Character {
+ name: String
+}
+
+type Droid implements Character {
+ name: String
+ function: String
+}
+
+type Human implements Character {
+ name: String
+ bornIn: String
+}
+```
diff --git a/docs/pages/guide/mutation.md b/docs/pages/guide/mutation.md
new file mode 100644
index 00000000..788cadfd
--- /dev/null
+++ b/docs/pages/guide/mutation.md
@@ -0,0 +1,67 @@
+# Mutations
+
+A Mutation is a special ObjectType that define also an Input.
+
+## Quick example
+
+This example model defines a Mutation:
+
+```python
+import graphene
+
+class CreatePerson(graphene.Mutation):
+ class Input:
+ name = graphene.String()
+
+ ok = graphene.String()
+ person = graphene.Field('Person')
+
+ @classmethod
+ def mutate(cls, args, info):
+ person = Person(name=args.get('name'))
+ ok = True
+ return CreatePerson(person=person, ok=ok)
+```
+
+**person** and **ok** are the output fields of the Mutation when is resolved.
+**Input** attributes are the arguments that the Mutation needs for resolving. **mutate** is the function that will be applied once the mutation is called.
+
+So, we can finish our schema like this:
+
+```python
+# ... the Mutation Class
+
+class Person(graphene.ObjectType):
+ name = graphene.String()
+
+class MyMutations(graphene.ObjectType):
+ create_person = graphene.Field(CreatePerson)
+
+schema = graphene.Schema(mutation=MyMutations)
+```
+
+## Executing the Mutation
+
+Then, if we query (`schema.execute(query_str)`) the following:
+```graphql
+mutation myFirstMutation {
+ createPerson(name:"Peter") {
+ person {
+ name
+ }
+ ok
+ }
+}
+```
+
+We should receive:
+
+```json
+{
+ "createPerson": {
+ "person" : {
+ name: "Peter"
+ },
+ "ok": true
+ }
+}
diff --git a/docs/pages/guide/objecttype.md b/docs/pages/guide/objecttype.md
new file mode 100644
index 00000000..191094ae
--- /dev/null
+++ b/docs/pages/guide/objecttype.md
@@ -0,0 +1,45 @@
+# ObjectTypes
+
+An ObjectType is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re querying.
+
+The basics:
+
+- Each ObjectType is a Python class that inherits graphene.ObjectType or inherits an implemented [interface](/docs/interface).
+- Each attribute of the ObjectType represents a GraphQL field.
+
+## Quick example
+
+This example model defines a Person, which has a first_name and last_name:
+
+```python
+import graphene
+
+class Person(graphene.ObjectType):
+ first_name = graphene.String()
+ last_name = graphene.String()
+ full_name = graphene.String()
+
+ def resolve_full_name(self, args, info):
+ return '{} {}'.format(self.first_name, self.last_name)
+```
+
+**first_name** and **last_name** are fields of the ObjectType. Each field is specified as a class attribute, and each attribute maps to a GraphQL field.
+
+The above `Person` ObjectType would have the following representation in a schema:
+
+```graphql
+type Person {
+ firstName: String
+ lastName: String
+ fullName: String
+}
+```
+
+## Instances as containers
+
+Graphene `ObjectType`s could act as containers too.
+So with the previous example you could do.
+
+```python
+peter = Person(first_name='Peter', last_name='Griffin')
+```
\ No newline at end of file
diff --git a/docs/pages/index.md b/docs/pages/index.md
new file mode 100644
index 00000000..80444d40
--- /dev/null
+++ b/docs/pages/index.md
@@ -0,0 +1,14 @@
+---
+path: /
+---
+Check our starwars API example!
+
+## About Graphene
+
+Graphene is a Python library for building GraphQL schemas/types fast and easily.
+
+**What is GraphQL?** A GraphQL query is a string interpreted by a server that returns data in a specified format.
+
+- **Easy to use:** Graphene helps you use GraphQL in Python without effort.
+- **Relay:** Graphene has builtin support for Relay
+- **Django:** Automatic *Django model* mapping to Graphene Types.
diff --git a/docs/vendor/glfx.optim.js b/docs/vendor/glfx.optim.js
new file mode 100644
index 00000000..dc489f10
--- /dev/null
+++ b/docs/vendor/glfx.optim.js
@@ -0,0 +1,796 @@
+/*
+ * glfx.js
+ * http://evanw.github.com/glfx.js/
+ *
+ * Copyright 2011 Evan Wallace
+ * Released under the MIT license
+ */
+module.exports = function() {
+ function q(a, d, c) {
+ return Math.max(a, Math.min(d, c))
+ }
+
+ function w(b) {
+ return {
+ _: b,
+ loadContentsOf: function(b) {
+ a = this._.gl;
+ this._.loadContentsOf(b)
+ },
+ destroy: function() {
+ a = this._.gl;
+ this._.destroy()
+ }
+ }
+ }
+
+ function A(a) {
+ return w(r.fromElement(a))
+ }
+
+ function B(b, d) {
+ var c = a.UNSIGNED_BYTE;
+ if (a.getExtension("OES_texture_float") && a.getExtension("OES_texture_float_linear")) {
+ var e = new r(100, 100, a.RGBA, a.FLOAT);
+ try {
+ e.drawTo(function() {
+ c = a.FLOAT
+ })
+ } catch (g) {}
+ e.destroy()
+ }
+ this._.texture && this._.texture.destroy();
+ this._.spareTexture && this._.spareTexture.destroy();
+ this.width = b;
+ this.height = d;
+ this._.texture = new r(b, d, a.RGBA, c);
+ this._.spareTexture = new r(b, d, a.RGBA, c);
+ this._.extraTexture = this._.extraTexture || new r(0, 0, a.RGBA, c);
+ this._.flippedShader = this._.flippedShader || new h(null, "uniform sampler2D texture;varying vec2 texCoord;void main(){gl_FragColor=texture2D(texture,vec2(texCoord.x,1.0-texCoord.y));}");
+ this._.isInitialized = !0
+ }
+
+ function C(a, d, c) {
+ this._.isInitialized &&
+ a._.width == this.width && a._.height == this.height || B.call(this, d ? d : a._.width, c ? c : a._.height);
+ a._.use();
+ this._.texture.drawTo(function() {
+ h.getDefaultShader().drawRect()
+ });
+ return this
+ }
+
+ function D() {
+ this._.texture.use();
+ this._.flippedShader.drawRect();
+ return this
+ }
+
+ function f(a, d, c, e) {
+ (c || this._.texture).use();
+ this._.spareTexture.drawTo(function() {
+ a.uniforms(d).drawRect()
+ });
+ this._.spareTexture.swapWith(e || this._.texture)
+ }
+
+ function E(a) {
+ a.parentNode.insertBefore(this, a);
+ a.parentNode.removeChild(a);
+ return this
+ }
+
+ function F() {
+ var b = new r(this._.texture.width, this._.texture.height, a.RGBA, a.UNSIGNED_BYTE);
+ this._.texture.use();
+ b.drawTo(function() {
+ h.getDefaultShader().drawRect()
+ });
+ return w(b)
+ }
+
+ function G() {
+ var b = this._.texture.width,
+ d = this._.texture.height,
+ c = new Uint8Array(4 * b * d);
+ this._.texture.drawTo(function() {
+ a.readPixels(0, 0, b, d, a.RGBA, a.UNSIGNED_BYTE, c)
+ });
+ return c
+ }
+
+ function k(b) {
+ return function() {
+ a = this._.gl;
+ return b.apply(this, arguments)
+ }
+ }
+
+ function x(a, d, c, e, g, l, n, p) {
+ var m = c - g,
+ h = e - l,
+ f = n - g,
+ k = p - l;
+ g = a - c + g - n;
+ l =
+ d - e + l - p;
+ var q = m * k - f * h,
+ f = (g * k - f * l) / q,
+ m = (m * l - g * h) / q;
+ return [c - a + f * c, e - d + f * e, f, n - a + m * n, p - d + m * p, m, a, d, 1]
+ }
+
+ function y(a) {
+ var d = a[0],
+ c = a[1],
+ e = a[2],
+ g = a[3],
+ l = a[4],
+ n = a[5],
+ p = a[6],
+ m = a[7];
+ a = a[8];
+ var f = d * l * a - d * n * m - c * g * a + c * n * p + e * g * m - e * l * p;
+ return [(l * a - n * m) / f, (e * m - c * a) / f, (c * n - e * l) / f, (n * p - g * a) / f, (d * a - e * p) / f, (e * g - d * n) / f, (g * m - l * p) / f, (c * p - d * m) / f, (d * l - c * g) / f]
+ }
+
+ function z(a) {
+ var d = a.length;
+ this.xa = [];
+ this.ya = [];
+ this.u = [];
+ this.y2 = [];
+ a.sort(function(a, b) {
+ return a[0] - b[0]
+ });
+ for (var c = 0; c < d; c++) this.xa.push(a[c][0]), this.ya.push(a[c][1]);
+ this.u[0] = 0;
+ this.y2[0] = 0;
+ for (c = 1; c < d - 1; ++c) {
+ a = this.xa[c + 1] - this.xa[c - 1];
+ var e = (this.xa[c] - this.xa[c - 1]) / a,
+ g = e * this.y2[c - 1] + 2;
+ this.y2[c] = (e - 1) / g;
+ this.u[c] = (6 * ((this.ya[c + 1] - this.ya[c]) / (this.xa[c + 1] - this.xa[c]) - (this.ya[c] - this.ya[c - 1]) / (this.xa[c] - this.xa[c - 1])) / a - e * this.u[c - 1]) / g
+ }
+ this.y2[d - 1] = 0;
+ for (c = d - 2; 0 <= c; --c) this.y2[c] = this.y2[c] * this.y2[c + 1] + this.u[c]
+ }
+
+ function u(a, d) {
+ return new h(null, a + "uniform sampler2D texture;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 coord=texCoord*texSize;" +
+ d + "gl_FragColor=texture2D(texture,coord/texSize);vec2 clampedCoord=clamp(coord,vec2(0.0),texSize);if(coord!=clampedCoord){gl_FragColor.a*=max(0.0,1.0-length(coord-clampedCoord));}}")
+ }
+
+ function H(b, d) {
+ a.brightnessContrast = a.brightnessContrast || new h(null, "uniform sampler2D texture;uniform float brightness;uniform float contrast;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.rgb+=brightness;if(contrast>0.0){color.rgb=(color.rgb-0.5)/(1.0-contrast)+0.5;}else{color.rgb=(color.rgb-0.5)*(1.0+contrast)+0.5;}gl_FragColor=color;}");
+ f.call(this, a.brightnessContrast, {
+ brightness: q(-1, b, 1),
+ contrast: q(-1, d, 1)
+ });
+ return this
+ }
+
+ function t(a) {
+ a = new z(a);
+ for (var d = [], c = 0; 256 > c; c++) d.push(q(0, Math.floor(256 * a.interpolate(c / 255)), 255));
+ return d
+ }
+
+ function I(b, d, c) {
+ b = t(b);
+ 1 == arguments.length ? d = c = b : (d = t(d), c = t(c));
+ for (var e = [], g = 0; 256 > g; g++) e.splice(e.length, 0, b[g], d[g], c[g], 255);
+ this._.extraTexture.initFromBytes(256, 1, e);
+ this._.extraTexture.use(1);
+ a.curves = a.curves || new h(null, "uniform sampler2D texture;uniform sampler2D map;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.r=texture2D(map,vec2(color.r)).r;color.g=texture2D(map,vec2(color.g)).g;color.b=texture2D(map,vec2(color.b)).b;gl_FragColor=color;}");
+ a.curves.textures({
+ map: 1
+ });
+ f.call(this, a.curves, {});
+ return this
+ }
+
+ function J(b) {
+ a.denoise = a.denoise || new h(null, "uniform sampler2D texture;uniform float exponent;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;void main(){vec4 center=texture2D(texture,texCoord);vec4 color=vec4(0.0);float total=0.0;for(float x=-4.0;x<=4.0;x+=1.0){for(float y=-4.0;y<=4.0;y+=1.0){vec4 sample=texture2D(texture,texCoord+vec2(x,y)/texSize);float weight=1.0-abs(dot(sample.rgb-center.rgb,vec3(0.25)));weight=pow(weight,exponent);color+=sample*weight;total+=weight;}}gl_FragColor=color/total;}");
+ for (var d = 0; 2 > d; d++) f.call(this, a.denoise, {
+ exponent: Math.max(0, b),
+ texSize: [this.width, this.height]
+ });
+ return this
+ }
+
+ function K(b, d) {
+ a.hueSaturation = a.hueSaturation || new h(null, "uniform sampler2D texture;uniform float hue;uniform float saturation;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float angle=hue*3.14159265;float s=sin(angle),c=cos(angle);vec3 weights=(vec3(2.0*c,-sqrt(3.0)*s-c,sqrt(3.0)*s-c)+1.0)/3.0;float len=length(color.rgb);color.rgb=vec3(dot(color.rgb,weights.xyz),dot(color.rgb,weights.zxy),dot(color.rgb,weights.yzx));float average=(color.r+color.g+color.b)/3.0;if(saturation>0.0){color.rgb+=(average-color.rgb)*(1.0-1.0/(1.001-saturation));}else{color.rgb+=(average-color.rgb)*(-saturation);}gl_FragColor=color;}");
+ f.call(this, a.hueSaturation, {
+ hue: q(-1, b, 1),
+ saturation: q(-1, d, 1)
+ });
+ return this
+ }
+
+ function L(b) {
+ a.noise = a.noise || new h(null, "uniform sampler2D texture;uniform float amount;varying vec2 texCoord;float rand(vec2 co){return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453);}void main(){vec4 color=texture2D(texture,texCoord);float diff=(rand(texCoord)-0.5)*amount;color.r+=diff;color.g+=diff;color.b+=diff;gl_FragColor=color;}");
+ f.call(this, a.noise, {
+ amount: q(0, b, 1)
+ });
+ return this
+ }
+
+ function M(b) {
+ a.sepia = a.sepia || new h(null, "uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float r=color.r;float g=color.g;float b=color.b;color.r=min(1.0,(r*(1.0-(0.607*amount)))+(g*(0.769*amount))+(b*(0.189*amount)));color.g=min(1.0,(r*0.349*amount)+(g*(1.0-(0.314*amount)))+(b*0.168*amount));color.b=min(1.0,(r*0.272*amount)+(g*0.534*amount)+(b*(1.0-(0.869*amount))));gl_FragColor=color;}");
+ f.call(this, a.sepia, {
+ amount: q(0, b, 1)
+ });
+ return this
+ }
+
+ function N(b, d) {
+ a.unsharpMask = a.unsharpMask || new h(null, "uniform sampler2D blurredTexture;uniform sampler2D originalTexture;uniform float strength;uniform float threshold;varying vec2 texCoord;void main(){vec4 blurred=texture2D(blurredTexture,texCoord);vec4 original=texture2D(originalTexture,texCoord);gl_FragColor=mix(blurred,original,1.0+strength);}");
+ this._.extraTexture.ensureFormat(this._.texture);
+ this._.texture.use();
+ this._.extraTexture.drawTo(function() {
+ h.getDefaultShader().drawRect()
+ });
+ this._.extraTexture.use(1);
+ this.triangleBlur(b);
+ a.unsharpMask.textures({
+ originalTexture: 1
+ });
+ f.call(this, a.unsharpMask, {
+ strength: d
+ });
+ this._.extraTexture.unuse(1);
+ return this
+ }
+
+ function O(b) {
+ a.vibrance = a.vibrance || new h(null, "uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;float mx=max(color.r,max(color.g,color.b));float amt=(mx-average)*(-amount*3.0);color.rgb=mix(color.rgb,vec3(mx),amt);gl_FragColor=color;}");
+ f.call(this, a.vibrance, {
+ amount: q(-1, b, 1)
+ });
+ return this
+ }
+
+ function P(b, d) {
+ a.vignette = a.vignette || new h(null, "uniform sampler2D texture;uniform float size;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float dist=distance(texCoord,vec2(0.5,0.5));color.rgb*=smoothstep(0.8,size*0.799,dist*(amount+size));gl_FragColor=color;}");
+ f.call(this, a.vignette, {
+ size: q(0, b, 1),
+ amount: q(0, d, 1)
+ });
+ return this
+ }
+
+ function Q(b, d, c) {
+ a.lensBlurPrePass = a.lensBlurPrePass || new h(null, "uniform sampler2D texture;uniform float power;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color=pow(color,vec4(power));gl_FragColor=vec4(color);}");
+ var e = "uniform sampler2D texture0;uniform sampler2D texture1;uniform vec2 delta0;uniform vec2 delta1;uniform float power;varying vec2 texCoord;" +
+ s + "vec4 sample(vec2 delta){float offset=random(vec3(delta,151.7182),0.0);vec4 color=vec4(0.0);float total=0.0;for(float t=0.0;t<=30.0;t++){float percent=(t+offset)/30.0;color+=texture2D(texture0,texCoord+delta*percent);total+=1.0;}return color/total;}";
+ a.lensBlur0 = a.lensBlur0 || new h(null, e + "void main(){gl_FragColor=sample(delta0);}");
+ a.lensBlur1 = a.lensBlur1 || new h(null, e + "void main(){gl_FragColor=(sample(delta0)+sample(delta1))*0.5;}");
+ a.lensBlur2 = a.lensBlur2 || (new h(null, e + "void main(){vec4 color=(sample(delta0)+2.0*texture2D(texture1,texCoord))/3.0;gl_FragColor=pow(color,vec4(power));}")).textures({
+ texture1: 1
+ });
+ for (var e = [], g = 0; 3 > g; g++) {
+ var l = c + 2 * g * Math.PI / 3;
+ e.push([b * Math.sin(l) / this.width, b * Math.cos(l) / this.height])
+ }
+ b = Math.pow(10, q(-1, d, 1));
+ f.call(this, a.lensBlurPrePass, {
+ power: b
+ });
+ this._.extraTexture.ensureFormat(this._.texture);
+ f.call(this, a.lensBlur0, {
+ delta0: e[0]
+ }, this._.texture, this._.extraTexture);
+ f.call(this, a.lensBlur1, {
+ delta0: e[1],
+ delta1: e[2]
+ }, this._.extraTexture, this._.extraTexture);
+ f.call(this, a.lensBlur0, {
+ delta0: e[1]
+ });
+ this._.extraTexture.use(1);
+ f.call(this, a.lensBlur2, {
+ power: 1 / b,
+ delta0: e[2]
+ });
+ return this
+ }
+
+ function R(b, d, c, e, g, l) {
+ a.tiltShift = a.tiltShift || new h(null, "uniform sampler2D texture;uniform float blurRadius;uniform float gradientRadius;uniform vec2 start;uniform vec2 end;uniform vec2 delta;uniform vec2 texSize;varying vec2 texCoord;" + s + "void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);vec2 normal=normalize(vec2(start.y-end.y,end.x-start.x));float radius=smoothstep(0.0,1.0,abs(dot(texCoord*texSize-start,normal))/gradientRadius)*blurRadius;for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta/texSize*percent*radius);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
+ var n = c - b,
+ p = e - d,
+ m = Math.sqrt(n * n + p * p);
+ f.call(this, a.tiltShift, {
+ blurRadius: g,
+ gradientRadius: l,
+ start: [b, d],
+ end: [c, e],
+ delta: [n / m, p / m],
+ texSize: [this.width, this.height]
+ });
+ f.call(this, a.tiltShift, {
+ blurRadius: g,
+ gradientRadius: l,
+ start: [b, d],
+ end: [c, e],
+ delta: [-p / m, n / m],
+ texSize: [this.width, this.height]
+ });
+ return this
+ }
+
+ function S(b) {
+ a.triangleBlur = a.triangleBlur || new h(null, "uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;" + s + "void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta*percent);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
+ f.call(this, a.triangleBlur, {
+ delta: [b / this.width, 0]
+ });
+ f.call(this, a.triangleBlur, {
+ delta: [0, b / this.height]
+ });
+ return this
+ }
+
+ function T(b, d, c) {
+ a.zoomBlur = a.zoomBlur || new h(null, "uniform sampler2D texture;uniform vec2 center;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;" + s + "void main(){vec4 color=vec4(0.0);float total=0.0;vec2 toCenter=center-texCoord*texSize;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=0.0;t<=40.0;t++){float percent=(t+offset)/40.0;float weight=4.0*(percent-percent*percent);vec4 sample=texture2D(texture,texCoord+toCenter*percent*strength/texSize);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
+ f.call(this, a.zoomBlur, {
+ center: [b, d],
+ strength: c,
+ texSize: [this.width, this.height]
+ });
+ return this
+ }
+
+ function U(b, d, c, e) {
+ a.colorHalftone = a.colorHalftone || new h(null, "uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(float angle){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);vec3 cmy=1.0-color.rgb;float k=min(cmy.x,min(cmy.y,cmy.z));cmy=(cmy-k)/(1.0-k);cmy=clamp(cmy*10.0-3.0+vec3(pattern(angle+0.26179),pattern(angle+1.30899),pattern(angle)),0.0,1.0);k=clamp(k*10.0-5.0+pattern(angle+0.78539),0.0,1.0);gl_FragColor=vec4(1.0-cmy-k,color.a);}");
+ f.call(this, a.colorHalftone, {
+ center: [b, d],
+ angle: c,
+ scale: Math.PI / e,
+ texSize: [this.width, this.height]
+ });
+ return this
+ }
+
+ function V(b, d, c, e) {
+ a.dotScreen = a.dotScreen || new h(null, "uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;gl_FragColor=vec4(vec3(average*10.0-5.0+pattern()),color.a);}");
+ f.call(this, a.dotScreen, {
+ center: [b, d],
+ angle: c,
+ scale: Math.PI / e,
+ texSize: [this.width, this.height]
+ });
+ return this
+ }
+
+ function W(b) {
+ a.edgeWork1 = a.edgeWork1 || new h(null, "uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;" + s + "void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec3 sample=texture2D(texture,texCoord+delta*percent).rgb;float average=(sample.r+sample.g+sample.b)/3.0;color.x+=average*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=average*weight;total.y+=weight;}}gl_FragColor=vec4(color/total,0.0,1.0);}");
+ a.edgeWork2 = a.edgeWork2 || new h(null, "uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;" + s + "void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec2 sample=texture2D(texture,texCoord+delta*percent).xy;color.x+=sample.x*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=sample.y*weight;total.y+=weight;}}float c=clamp(10000.0*(color.y/total.y-color.x/total.x)+0.5,0.0,1.0);gl_FragColor=vec4(c,c,c,1.0);}");
+ f.call(this, a.edgeWork1, {
+ delta: [b / this.width, 0]
+ });
+ f.call(this, a.edgeWork2, {
+ delta: [0, b / this.height]
+ });
+ return this
+ }
+
+ function X(b, d, c) {
+ a.hexagonalPixelate = a.hexagonalPixelate || new h(null, "uniform sampler2D texture;uniform vec2 center;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 tex=(texCoord*texSize-center)/scale;tex.y/=0.866025404;tex.x-=tex.y*0.5;vec2 a;if(tex.x+tex.y-floor(tex.x)-floor(tex.y)<1.0)a=vec2(floor(tex.x),floor(tex.y));else a=vec2(ceil(tex.x),ceil(tex.y));vec2 b=vec2(ceil(tex.x),floor(tex.y));vec2 c=vec2(floor(tex.x),ceil(tex.y));vec3 TEX=vec3(tex.x,tex.y,1.0-tex.x-tex.y);vec3 A=vec3(a.x,a.y,1.0-a.x-a.y);vec3 B=vec3(b.x,b.y,1.0-b.x-b.y);vec3 C=vec3(c.x,c.y,1.0-c.x-c.y);float alen=length(TEX-A);float blen=length(TEX-B);float clen=length(TEX-C);vec2 choice;if(alen0.0){coord*=mix(1.0,smoothstep(0.0,radius/distance,percent),strength*0.75);}else{coord*=mix(1.0,pow(percent,1.0+strength*0.75)*radius/distance,1.0-percent);}}coord+=center;");
+ f.call(this, a.bulgePinch, {
+ radius: c,
+ strength: q(-1, e, 1),
+ center: [b, d],
+ texSize: [this.width, this.height]
+ });
+ return this
+ }
+
+ function $(b, d, c) {
+ a.matrixWarp = a.matrixWarp || u("uniform mat3 matrix;uniform bool useTextureSpace;", "if(useTextureSpace)coord=coord/texSize*2.0-1.0;vec3 warp=matrix*vec3(coord,1.0);coord=warp.xy/warp.z;if(useTextureSpace)coord=(coord*0.5+0.5)*texSize;");
+ b = Array.prototype.concat.apply([], b);
+ if (4 == b.length) b = [b[0], b[1], 0, b[2], b[3], 0, 0, 0, 1];
+ else if (9 != b.length) throw "can only warp with 2x2 or 3x3 matrix";
+ f.call(this, a.matrixWarp, {
+ matrix: d ? y(b) : b,
+ texSize: [this.width, this.height],
+ useTextureSpace: c | 0
+ });
+ return this
+ }
+
+ function aa(a, d) {
+ var c = x.apply(null, d),
+ e = x.apply(null, a),
+ c = y(c);
+ return this.matrixWarp([c[0] * e[0] + c[1] * e[3] + c[2] * e[6], c[0] * e[1] + c[1] * e[4] + c[2] * e[7], c[0] * e[2] + c[1] * e[5] + c[2] * e[8], c[3] * e[0] + c[4] * e[3] + c[5] * e[6], c[3] * e[1] + c[4] * e[4] + c[5] * e[7], c[3] * e[2] + c[4] * e[5] + c[5] * e[8], c[6] * e[0] + c[7] * e[3] + c[8] * e[6],
+ c[6] * e[1] + c[7] * e[4] + c[8] * e[7], c[6] * e[2] + c[7] * e[5] + c[8] * e[8]
+ ])
+ }
+
+ function ba(b, d, c, e) {
+ a.swirl = a.swirl || u("uniform float radius;uniform float angle;uniform vec2 center;", "coord-=center;float distance=length(coord);if(distance> 1;
+ this.xa[e] > a ? c = e : d = e
+ }
+ var e = this.xa[c] -
+ this.xa[d],
+ g = (this.xa[c] - a) / e;
+ a = (a - this.xa[d]) / e;
+ return g * this.ya[d] + a * this.ya[c] + ((g * g * g - g) * this.y2[d] + (a * a * a - a) * this.y2[c]) * e * e / 6
+ };
+ var r = function() {
+ function b(b, c, d, f) {
+ this.gl = a;
+ this.id = a.createTexture();
+ this.width = b;
+ this.height = c;
+ this.format = d;
+ this.type = f;
+ a.bindTexture(a.TEXTURE_2D, this.id);
+ a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MAG_FILTER, a.LINEAR);
+ a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MIN_FILTER, a.LINEAR);
+ a.texParameteri(a.TEXTURE_2D, a.TEXTURE_WRAP_S, a.CLAMP_TO_EDGE);
+ a.texParameteri(a.TEXTURE_2D,
+ a.TEXTURE_WRAP_T, a.CLAMP_TO_EDGE);
+ b && c && a.texImage2D(a.TEXTURE_2D, 0, this.format, b, c, 0, this.format, this.type, null)
+ }
+
+ function d(a) {
+ null == c && (c = document.createElement("canvas"));
+ c.width = a.width;
+ c.height = a.height;
+ a = c.getContext("2d");
+ a.clearRect(0, 0, c.width, c.height);
+ return a
+ }
+ b.fromElement = function(c) {
+ var d = new b(0, 0, a.RGBA, a.UNSIGNED_BYTE);
+ d.loadContentsOf(c);
+ return d
+ };
+ b.prototype.loadContentsOf = function(b) {
+ this.width = b.width || b.videoWidth;
+ this.height = b.height || b.videoHeight;
+ a.bindTexture(a.TEXTURE_2D,
+ this.id);
+ a.texImage2D(a.TEXTURE_2D, 0, this.format, this.format, this.type, b)
+ };
+ b.prototype.initFromBytes = function(b, c, d) {
+ this.width = b;
+ this.height = c;
+ this.format = a.RGBA;
+ this.type = a.UNSIGNED_BYTE;
+ a.bindTexture(a.TEXTURE_2D, this.id);
+ a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, b, c, 0, a.RGBA, this.type, new Uint8Array(d))
+ };
+ b.prototype.initFromCanvas = function(b, c, d) {
+ this.width = b;
+ this.height = c;
+ this.format = a.RGB;
+ this.type = a.UNSIGNED_BYTE;
+ a.bindTexture(a.TEXTURE_2D, this.id);
+ // a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, b, c, 0, a.RGBA, this.type, d);
+ a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, a.RGBA, a.UNSIGNED_BYTE, d);
+ };
+ b.prototype.destroy = function() {
+ a.deleteTexture(this.id);
+ this.id = null
+ };
+ b.prototype.use = function(b) {
+ a.activeTexture(a.TEXTURE0 + (b || 0));
+ a.bindTexture(a.TEXTURE_2D, this.id)
+ };
+ b.prototype.unuse = function(b) {
+ a.activeTexture(a.TEXTURE0 +
+ (b || 0));
+ a.bindTexture(a.TEXTURE_2D, null)
+ };
+ b.prototype.ensureFormat = function(b, c, d, f) {
+ if (1 == arguments.length) {
+ var h = arguments[0];
+ b = h.width;
+ c = h.height;
+ d = h.format;
+ f = h.type
+ }
+ if (b != this.width || c != this.height || d != this.format || f != this.type) this.width = b, this.height = c, this.format = d, this.type = f, a.bindTexture(a.TEXTURE_2D, this.id), a.texImage2D(a.TEXTURE_2D, 0, this.format, b, c, 0, this.format, this.type, null)
+ };
+ b.prototype.drawTo = function(b) {
+ a.framebuffer = a.framebuffer || a.createFramebuffer();
+ a.bindFramebuffer(a.FRAMEBUFFER,
+ a.framebuffer);
+ a.framebufferTexture2D(a.FRAMEBUFFER, a.COLOR_ATTACHMENT0, a.TEXTURE_2D, this.id, 0);
+ if (a.checkFramebufferStatus(a.FRAMEBUFFER) !== a.FRAMEBUFFER_COMPLETE) throw Error("incomplete framebuffer");
+ a.viewport(0, 0, this.width, this.height);
+ b();
+ a.bindFramebuffer(a.FRAMEBUFFER, null)
+ };
+ var c = null;
+ b.prototype.fillUsingCanvas = function(b) {
+ b(d(this));
+ this.format = a.RGBA;
+ this.type = a.UNSIGNED_BYTE;
+ a.bindTexture(a.TEXTURE_2D, this.id);
+ a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, a.RGBA, a.UNSIGNED_BYTE, c);
+ return this
+ };
+ b.prototype.toImage = function(b) {
+ this.use();
+ h.getDefaultShader().drawRect();
+ var f = 4 * this.width * this.height,
+ k = new Uint8Array(f),
+ n = d(this),
+ p = n.createImageData(this.width, this.height);
+ a.readPixels(0, 0, this.width, this.height, a.RGBA, a.UNSIGNED_BYTE, k);
+ for (var m = 0; m < f; m++) p.data[m] = k[m];
+ n.putImageData(p, 0, 0);
+ b.src = c.toDataURL()
+ };
+ b.prototype.swapWith = function(a) {
+ var b;
+ b = a.id;
+ a.id = this.id;
+ this.id = b;
+ b = a.width;
+ a.width = this.width;
+ this.width = b;
+ b = a.height;
+ a.height = this.height;
+ this.height = b;
+ b = a.format;
+ a.format =
+ this.format;
+ this.format = b
+ };
+ return b
+ }(),
+ s = "float random(vec3 scale,float seed){return fract(sin(dot(gl_FragCoord.xyz+seed,scale))*43758.5453+seed);}";
+ return v
+}();
\ No newline at end of file
diff --git a/docs/vendor/particles.js b/docs/vendor/particles.js
new file mode 100644
index 00000000..240e8b82
--- /dev/null
+++ b/docs/vendor/particles.js
@@ -0,0 +1,1545 @@
+/* -----------------------------------------------
+/* Author : Vincent Garreau - vincentgarreau.com
+/* MIT license: http://opensource.org/licenses/MIT
+/* Demo / Generator : vincentgarreau.com/particles.js
+/* GitHub : github.com/VincentGarreau/particles.js
+/* How to use? : Check the GitHub README
+/* v2.0.0
+/* ----------------------------------------------- */
+
+var pJS = function(tag_id, params){
+
+ var canvas_el = document.querySelector('#'+tag_id+' > .particles-js-canvas-el');
+
+ /* particles.js variables with default values */
+ this.pJS = {
+ canvas: {
+ el: canvas_el,
+ w: canvas_el.offsetWidth,
+ h: canvas_el.offsetHeight
+ },
+ particles: {
+ number: {
+ value: 400,
+ density: {
+ enable: true,
+ value_area: 800
+ }
+ },
+ color: {
+ value: '#fff'
+ },
+ shape: {
+ type: 'circle',
+ stroke: {
+ width: 0,
+ color: '#ff0000'
+ },
+ polygon: {
+ nb_sides: 5
+ },
+ image: {
+ src: '',
+ width: 100,
+ height: 100
+ }
+ },
+ opacity: {
+ value: 1,
+ random: false,
+ anim: {
+ enable: false,
+ speed: 2,
+ opacity_min: 0,
+ sync: false
+ }
+ },
+ size: {
+ value: 20,
+ random: false,
+ anim: {
+ enable: false,
+ speed: 20,
+ size_min: 0,
+ sync: false
+ }
+ },
+ line_linked: {
+ enable: true,
+ distance: 100,
+ color: '#fff',
+ opacity: 1,
+ width: 1
+ },
+ move: {
+ enable: true,
+ speed: 2,
+ direction: 'none',
+ random: false,
+ straight: false,
+ out_mode: 'out',
+ bounce: false,
+ attract: {
+ enable: false,
+ rotateX: 3000,
+ rotateY: 3000
+ }
+ },
+ array: []
+ },
+ interactivity: {
+ detect_on: 'canvas',
+ events: {
+ onhover: {
+ enable: true,
+ mode: 'grab'
+ },
+ onclick: {
+ enable: true,
+ mode: 'push'
+ },
+ resize: true
+ },
+ modes: {
+ grab:{
+ distance: 100,
+ line_linked:{
+ opacity: 1
+ }
+ },
+ bubble:{
+ distance: 200,
+ size: 80,
+ duration: 0.4
+ },
+ repulse:{
+ distance: 200,
+ duration: 0.4
+ },
+ push:{
+ particles_nb: 4
+ },
+ remove:{
+ particles_nb: 2
+ }
+ },
+ mouse:{}
+ },
+ retina_detect: false,
+ fn: {
+ interact: {},
+ modes: {},
+ vendors:{}
+ },
+ tmp: {}
+ };
+
+ var pJS = this.pJS;
+
+ /* params settings */
+ if(params){
+ Object.deepExtend(pJS, params);
+ }
+
+ pJS.tmp.obj = {
+ size_value: pJS.particles.size.value,
+ size_anim_speed: pJS.particles.size.anim.speed,
+ move_speed: pJS.particles.move.speed,
+ line_linked_distance: pJS.particles.line_linked.distance,
+ line_linked_width: pJS.particles.line_linked.width,
+ mode_grab_distance: pJS.interactivity.modes.grab.distance,
+ mode_bubble_distance: pJS.interactivity.modes.bubble.distance,
+ mode_bubble_size: pJS.interactivity.modes.bubble.size,
+ mode_repulse_distance: pJS.interactivity.modes.repulse.distance
+ };
+
+
+ pJS.fn.retinaInit = function(){
+
+ if(pJS.retina_detect && window.devicePixelRatio > 1){
+ pJS.canvas.pxratio = window.devicePixelRatio;
+ pJS.tmp.retina = true;
+ }
+ else{
+ pJS.canvas.pxratio = 1;
+ pJS.tmp.retina = false;
+ }
+
+ pJS.canvas.w = pJS.canvas.el.offsetWidth * pJS.canvas.pxratio;
+ pJS.canvas.h = pJS.canvas.el.offsetHeight * pJS.canvas.pxratio;
+
+ pJS.particles.size.value = pJS.tmp.obj.size_value * pJS.canvas.pxratio;
+ pJS.particles.size.anim.speed = pJS.tmp.obj.size_anim_speed * pJS.canvas.pxratio;
+ pJS.particles.move.speed = pJS.tmp.obj.move_speed * pJS.canvas.pxratio;
+ pJS.particles.line_linked.distance = pJS.tmp.obj.line_linked_distance * pJS.canvas.pxratio;
+ pJS.interactivity.modes.grab.distance = pJS.tmp.obj.mode_grab_distance * pJS.canvas.pxratio;
+ pJS.interactivity.modes.bubble.distance = pJS.tmp.obj.mode_bubble_distance * pJS.canvas.pxratio;
+ pJS.particles.line_linked.width = pJS.tmp.obj.line_linked_width * pJS.canvas.pxratio;
+ pJS.interactivity.modes.bubble.size = pJS.tmp.obj.mode_bubble_size * pJS.canvas.pxratio;
+ pJS.interactivity.modes.repulse.distance = pJS.tmp.obj.mode_repulse_distance * pJS.canvas.pxratio;
+
+ };
+
+
+
+ /* ---------- pJS functions - canvas ------------ */
+
+ pJS.fn.canvasInit = function(){
+ pJS.canvas.ctx = pJS.canvas.el.getContext('2d');
+ };
+
+ pJS.fn.canvasSize = function(){
+
+ pJS.canvas.el.width = pJS.canvas.w;
+ pJS.canvas.el.height = pJS.canvas.h;
+
+ if(pJS && pJS.interactivity.events.resize){
+
+ window.addEventListener('resize', function(){
+
+ pJS.canvas.w = pJS.canvas.el.offsetWidth;
+ pJS.canvas.h = pJS.canvas.el.offsetHeight;
+
+ /* resize canvas */
+ if(pJS.tmp.retina){
+ pJS.canvas.w *= pJS.canvas.pxratio;
+ pJS.canvas.h *= pJS.canvas.pxratio;
+ }
+
+ pJS.canvas.el.width = pJS.canvas.w;
+ pJS.canvas.el.height = pJS.canvas.h;
+
+ /* repaint canvas on anim disabled */
+ if(!pJS.particles.move.enable){
+ pJS.fn.particlesEmpty();
+ pJS.fn.particlesCreate();
+ pJS.fn.particlesDraw();
+ pJS.fn.vendors.densityAutoParticles();
+ }
+
+ /* density particles enabled */
+ pJS.fn.vendors.densityAutoParticles();
+
+ });
+
+ }
+
+ };
+
+
+ pJS.fn.canvasPaint = function(){
+ pJS.canvas.ctx.fillRect(0, 0, pJS.canvas.w, pJS.canvas.h);
+ };
+
+ pJS.fn.canvasClear = function(){
+ pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h);
+ };
+
+
+ /* --------- pJS functions - particles ----------- */
+
+ pJS.fn.particle = function(color, opacity, position){
+
+ /* size */
+ this.radius = (pJS.particles.size.random ? Math.random() : 1) * pJS.particles.size.value;
+ if(pJS.particles.size.anim.enable){
+ this.size_status = false;
+ this.vs = pJS.particles.size.anim.speed / 100;
+ if(!pJS.particles.size.anim.sync){
+ this.vs = this.vs * Math.random();
+ }
+ }
+
+ /* position */
+ this.x = position ? position.x : Math.random() * pJS.canvas.w;
+ this.y = position ? position.y : Math.random() * pJS.canvas.h;
+
+ /* check position - into the canvas */
+ if(this.x > pJS.canvas.w - this.radius*2) this.x = this.x - this.radius;
+ else if(this.x < this.radius*2) this.x = this.x + this.radius;
+ if(this.y > pJS.canvas.h - this.radius*2) this.y = this.y - this.radius;
+ else if(this.y < this.radius*2) this.y = this.y + this.radius;
+
+ /* check position - avoid overlap */
+ if(pJS.particles.move.bounce){
+ pJS.fn.vendors.checkOverlap(this, position);
+ }
+
+ /* color */
+ this.color = {};
+ if(typeof(color.value) == 'object'){
+
+ if(color.value instanceof Array){
+ var color_selected = color.value[Math.floor(Math.random() * pJS.particles.color.value.length)];
+ this.color.rgb = hexToRgb(color_selected);
+ }else{
+ if(color.value.r != undefined && color.value.g != undefined && color.value.b != undefined){
+ this.color.rgb = {
+ r: color.value.r,
+ g: color.value.g,
+ b: color.value.b
+ }
+ }
+ if(color.value.h != undefined && color.value.s != undefined && color.value.l != undefined){
+ this.color.hsl = {
+ h: color.value.h,
+ s: color.value.s,
+ l: color.value.l
+ }
+ }
+ }
+
+ }
+ else if(color.value == 'random'){
+ this.color.rgb = {
+ r: (Math.floor(Math.random() * (255 - 0 + 1)) + 0),
+ g: (Math.floor(Math.random() * (255 - 0 + 1)) + 0),
+ b: (Math.floor(Math.random() * (255 - 0 + 1)) + 0)
+ }
+ }
+ else if(typeof(color.value) == 'string'){
+ this.color = color;
+ this.color.rgb = hexToRgb(this.color.value);
+ }
+
+ /* opacity */
+ this.opacity = (pJS.particles.opacity.random ? Math.random() : 1) * pJS.particles.opacity.value;
+ if(pJS.particles.opacity.anim.enable){
+ this.opacity_status = false;
+ this.vo = pJS.particles.opacity.anim.speed / 100;
+ if(!pJS.particles.opacity.anim.sync){
+ this.vo = this.vo * Math.random();
+ }
+ }
+
+ /* animation - velocity for speed */
+ var velbase = {}
+ switch(pJS.particles.move.direction){
+ case 'top':
+ velbase = { x:0, y:-1 };
+ break;
+ case 'top-right':
+ velbase = { x:0.5, y:-0.5 };
+ break;
+ case 'right':
+ velbase = { x:1, y:-0 };
+ break;
+ case 'bottom-right':
+ velbase = { x:0.5, y:0.5 };
+ break;
+ case 'bottom':
+ velbase = { x:0, y:1 };
+ break;
+ case 'bottom-left':
+ velbase = { x:-0.5, y:1 };
+ break;
+ case 'left':
+ velbase = { x:-1, y:0 };
+ break;
+ case 'top-left':
+ velbase = { x:-0.5, y:-0.5 };
+ break;
+ default:
+ velbase = { x:0, y:0 };
+ break;
+ }
+
+ if(pJS.particles.move.straight){
+ this.vx = velbase.x;
+ this.vy = velbase.y;
+ if(pJS.particles.move.random){
+ this.vx = this.vx * (Math.random());
+ this.vy = this.vy * (Math.random());
+ }
+ }else{
+ this.vx = velbase.x + Math.random()-0.5;
+ this.vy = velbase.y + Math.random()-0.5;
+ }
+
+ // var theta = 2.0 * Math.PI * Math.random();
+ // this.vx = Math.cos(theta);
+ // this.vy = Math.sin(theta);
+
+ this.vx_i = this.vx;
+ this.vy_i = this.vy;
+
+
+
+ /* if shape is image */
+
+ var shape_type = pJS.particles.shape.type;
+ if(typeof(shape_type) == 'object'){
+ if(shape_type instanceof Array){
+ var shape_selected = shape_type[Math.floor(Math.random() * shape_type.length)];
+ this.shape = shape_selected;
+ }
+ }else{
+ this.shape = shape_type;
+ }
+
+ if(this.shape == 'image'){
+ var sh = pJS.particles.shape;
+ this.img = {
+ src: sh.image.src,
+ ratio: sh.image.width / sh.image.height
+ }
+ if(!this.img.ratio) this.img.ratio = 1;
+ if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg != undefined){
+ pJS.fn.vendors.createSvgImg(this);
+ if(pJS.tmp.pushing){
+ this.img.loaded = false;
+ }
+ }
+ }
+
+
+
+ };
+
+
+ pJS.fn.particle.prototype.draw = function() {
+
+ var p = this;
+
+ if(p.radius_bubble != undefined){
+ var radius = p.radius_bubble;
+ }else{
+ var radius = p.radius;
+ }
+
+ if(p.opacity_bubble != undefined){
+ var opacity = p.opacity_bubble;
+ }else{
+ var opacity = p.opacity;
+ }
+
+ if(p.color.rgb){
+ var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+opacity+')';
+ }else{
+ var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+opacity+')';
+ }
+
+ pJS.canvas.ctx.fillStyle = color_value;
+ pJS.canvas.ctx.beginPath();
+
+ switch(p.shape){
+
+ case 'circle':
+ pJS.canvas.ctx.arc(p.x, p.y, radius, 0, Math.PI * 2, false);
+ break;
+
+ case 'edge':
+ pJS.canvas.ctx.rect(p.x-radius, p.y-radius, radius*2, radius*2);
+ break;
+
+ case 'triangle':
+ pJS.fn.vendors.drawShape(pJS.canvas.ctx, p.x-radius, p.y+radius / 1.66, radius*2, 3, 2);
+ break;
+
+ case 'polygon':
+ pJS.fn.vendors.drawShape(
+ pJS.canvas.ctx,
+ p.x - radius / (pJS.particles.shape.polygon.nb_sides/3.5), // startX
+ p.y - radius / (2.66/3.5), // startY
+ radius*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength
+ pJS.particles.shape.polygon.nb_sides, // sideCountNumerator
+ 1 // sideCountDenominator
+ );
+ break;
+
+ case 'star':
+ pJS.fn.vendors.drawShape(
+ pJS.canvas.ctx,
+ p.x - radius*2 / (pJS.particles.shape.polygon.nb_sides/4), // startX
+ p.y - radius / (2*2.66/3.5), // startY
+ radius*2*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength
+ pJS.particles.shape.polygon.nb_sides, // sideCountNumerator
+ 2 // sideCountDenominator
+ );
+ break;
+
+ case 'image':
+
+ function draw(){
+ pJS.canvas.ctx.drawImage(
+ img_obj,
+ p.x-radius,
+ p.y-radius,
+ radius*2,
+ radius*2 / p.img.ratio
+ );
+ }
+
+ if(pJS.tmp.img_type == 'svg'){
+ var img_obj = p.img.obj;
+ }else{
+ var img_obj = pJS.tmp.img_obj;
+ }
+
+ if(img_obj){
+ draw();
+ }
+
+ break;
+
+ }
+
+ pJS.canvas.ctx.closePath();
+
+ if(pJS.particles.shape.stroke.width > 0){
+ pJS.canvas.ctx.strokeStyle = pJS.particles.shape.stroke.color;
+ pJS.canvas.ctx.lineWidth = pJS.particles.shape.stroke.width;
+ pJS.canvas.ctx.stroke();
+ }
+
+ pJS.canvas.ctx.fill();
+
+ };
+
+
+ pJS.fn.particlesCreate = function(){
+ for(var i = 0; i < pJS.particles.number.value; i++) {
+ pJS.particles.array.push(new pJS.fn.particle(pJS.particles.color, pJS.particles.opacity.value));
+ }
+ };
+
+ pJS.fn.particlesUpdate = function(){
+
+ for(var i = 0; i < pJS.particles.array.length; i++){
+
+ /* the particle */
+ var p = pJS.particles.array[i];
+
+ // var d = ( dx = pJS.interactivity.mouse.click_pos_x - p.x ) * dx + ( dy = pJS.interactivity.mouse.click_pos_y - p.y ) * dy;
+ // var f = -BANG_SIZE / d;
+ // if ( d < BANG_SIZE ) {
+ // var t = Math.atan2( dy, dx );
+ // p.vx = f * Math.cos(t);
+ // p.vy = f * Math.sin(t);
+ // }
+
+ /* move the particle */
+ if(pJS.particles.move.enable){
+ var ms = pJS.particles.move.speed/2;
+ p.x += p.vx * ms;
+ p.y += p.vy * ms;
+ }
+
+ /* change opacity status */
+ if(pJS.particles.opacity.anim.enable) {
+ if(p.opacity_status == true) {
+ if(p.opacity >= pJS.particles.opacity.value) p.opacity_status = false;
+ p.opacity += p.vo;
+ }else {
+ if(p.opacity <= pJS.particles.opacity.anim.opacity_min) p.opacity_status = true;
+ p.opacity -= p.vo;
+ }
+ if(p.opacity < 0) p.opacity = 0;
+ }
+
+ /* change size */
+ if(pJS.particles.size.anim.enable){
+ if(p.size_status == true){
+ if(p.radius >= pJS.particles.size.value) p.size_status = false;
+ p.radius += p.vs;
+ }else{
+ if(p.radius <= pJS.particles.size.anim.size_min) p.size_status = true;
+ p.radius -= p.vs;
+ }
+ if(p.radius < 0) p.radius = 0;
+ }
+
+ /* change particle position if it is out of canvas */
+ if(pJS.particles.move.out_mode == 'bounce'){
+ var new_pos = {
+ x_left: p.radius,
+ x_right: pJS.canvas.w,
+ y_top: p.radius,
+ y_bottom: pJS.canvas.h
+ }
+ }else{
+ var new_pos = {
+ x_left: -p.radius,
+ x_right: pJS.canvas.w + p.radius,
+ y_top: -p.radius,
+ y_bottom: pJS.canvas.h + p.radius
+ }
+ }
+
+ if(p.x - p.radius > pJS.canvas.w){
+ p.x = new_pos.x_left;
+ p.y = Math.random() * pJS.canvas.h;
+ }
+ else if(p.x + p.radius < 0){
+ p.x = new_pos.x_right;
+ p.y = Math.random() * pJS.canvas.h;
+ }
+ if(p.y - p.radius > pJS.canvas.h){
+ p.y = new_pos.y_top;
+ p.x = Math.random() * pJS.canvas.w;
+ }
+ else if(p.y + p.radius < 0){
+ p.y = new_pos.y_bottom;
+ p.x = Math.random() * pJS.canvas.w;
+ }
+
+ /* out of canvas modes */
+ switch(pJS.particles.move.out_mode){
+ case 'bounce':
+ if (p.x + p.radius > pJS.canvas.w) p.vx = -p.vx;
+ else if (p.x - p.radius < 0) p.vx = -p.vx;
+ if (p.y + p.radius > pJS.canvas.h) p.vy = -p.vy;
+ else if (p.y - p.radius < 0) p.vy = -p.vy;
+ break;
+ }
+
+ /* events */
+ if(isInArray('grab', pJS.interactivity.events.onhover.mode)){
+ pJS.fn.modes.grabParticle(p);
+ }
+
+ if(isInArray('bubble', pJS.interactivity.events.onhover.mode) || isInArray('bubble', pJS.interactivity.events.onclick.mode)){
+ pJS.fn.modes.bubbleParticle(p);
+ }
+
+ if(isInArray('repulse', pJS.interactivity.events.onhover.mode) || isInArray('repulse', pJS.interactivity.events.onclick.mode)){
+ pJS.fn.modes.repulseParticle(p);
+ }
+
+ /* interaction auto between particles */
+ if(pJS.particles.line_linked.enable || pJS.particles.move.attract.enable){
+ for(var j = i + 1; j < pJS.particles.array.length; j++){
+ var p2 = pJS.particles.array[j];
+
+ /* link particles */
+ if(pJS.particles.line_linked.enable){
+ pJS.fn.interact.linkParticles(p,p2);
+ }
+
+ /* attract particles */
+ if(pJS.particles.move.attract.enable){
+ pJS.fn.interact.attractParticles(p,p2);
+ }
+
+ /* bounce particles */
+ if(pJS.particles.move.bounce){
+ pJS.fn.interact.bounceParticles(p,p2);
+ }
+
+ }
+ }
+
+
+ }
+
+ };
+
+ pJS.fn.particlesDraw = function(){
+
+ /* clear canvas */
+ pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h);
+
+ /* update each particles param */
+ pJS.fn.particlesUpdate();
+
+ /* draw each particle */
+ for(var i = 0; i < pJS.particles.array.length; i++){
+ var p = pJS.particles.array[i];
+ p.draw();
+ }
+
+ };
+
+ pJS.fn.particlesEmpty = function(){
+ pJS.particles.array = [];
+ };
+
+ pJS.fn.particlesRefresh = function(){
+
+ /* init all */
+ cancelRequestAnimFrame(pJS.fn.checkAnimFrame);
+ cancelRequestAnimFrame(pJS.fn.drawAnimFrame);
+ pJS.tmp.source_svg = undefined;
+ pJS.tmp.img_obj = undefined;
+ pJS.tmp.count_svg = 0;
+ pJS.fn.particlesEmpty();
+ pJS.fn.canvasClear();
+
+ /* restart */
+ pJS.fn.vendors.start();
+
+ };
+
+
+ /* ---------- pJS functions - particles interaction ------------ */
+
+ pJS.fn.interact.linkParticles = function(p1, p2){
+
+ var dx = p1.x - p2.x,
+ dy = p1.y - p2.y,
+ dist = Math.sqrt(dx*dx + dy*dy);
+
+ /* draw a line between p1 and p2 if the distance between them is under the config distance */
+ if(dist <= pJS.particles.line_linked.distance){
+
+ var opacity_line = pJS.particles.line_linked.opacity - (dist / (1/pJS.particles.line_linked.opacity)) / pJS.particles.line_linked.distance;
+
+ if(opacity_line > 0){
+
+ /* style */
+ var color_line = pJS.particles.line_linked.color_rgb_line;
+ pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')';
+ pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width;
+ //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */
+
+ /* path */
+ pJS.canvas.ctx.beginPath();
+ pJS.canvas.ctx.moveTo(p1.x, p1.y);
+ pJS.canvas.ctx.lineTo(p2.x, p2.y);
+ pJS.canvas.ctx.stroke();
+ pJS.canvas.ctx.closePath();
+
+ }
+
+ }
+
+ };
+
+
+ pJS.fn.interact.attractParticles = function(p1, p2){
+
+ /* condensed particles */
+ var dx = p1.x - p2.x,
+ dy = p1.y - p2.y,
+ dist = Math.sqrt(dx*dx + dy*dy);
+
+ if(dist <= pJS.particles.line_linked.distance){
+
+ var ax = dx/(pJS.particles.move.attract.rotateX*1000),
+ ay = dy/(pJS.particles.move.attract.rotateY*1000);
+
+ p1.vx -= ax;
+ p1.vy -= ay;
+
+ p2.vx += ax;
+ p2.vy += ay;
+
+ }
+
+
+ }
+
+
+ pJS.fn.interact.bounceParticles = function(p1, p2){
+
+ var dx = p1.x - p2.x,
+ dy = p1.y - p2.y,
+ dist = Math.sqrt(dx*dx + dy*dy),
+ dist_p = p1.radius+p2.radius;
+
+ if(dist <= dist_p){
+ p1.vx = -p1.vx;
+ p1.vy = -p1.vy;
+
+ p2.vx = -p2.vx;
+ p2.vy = -p2.vy;
+ }
+
+ }
+
+
+ /* ---------- pJS functions - modes events ------------ */
+
+ pJS.fn.modes.pushParticles = function(nb, pos){
+
+ pJS.tmp.pushing = true;
+
+ for(var i = 0; i < nb; i++){
+ pJS.particles.array.push(
+ new pJS.fn.particle(
+ pJS.particles.color,
+ pJS.particles.opacity.value,
+ {
+ 'x': pos ? pos.pos_x : Math.random() * pJS.canvas.w,
+ 'y': pos ? pos.pos_y : Math.random() * pJS.canvas.h
+ }
+ )
+ )
+ if(i == nb-1){
+ if(!pJS.particles.move.enable){
+ pJS.fn.particlesDraw();
+ }
+ pJS.tmp.pushing = false;
+ }
+ }
+
+ };
+
+
+ pJS.fn.modes.removeParticles = function(nb){
+
+ pJS.particles.array.splice(0, nb);
+ if(!pJS.particles.move.enable){
+ pJS.fn.particlesDraw();
+ }
+
+ };
+
+
+ pJS.fn.modes.bubbleParticle = function(p){
+
+ /* on hover event */
+ if(pJS.interactivity.events.onhover.enable && isInArray('bubble', pJS.interactivity.events.onhover.mode)){
+
+ var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,
+ dy_mouse = p.y - pJS.interactivity.mouse.pos_y,
+ dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse),
+ ratio = 1 - dist_mouse / pJS.interactivity.modes.bubble.distance;
+
+ function init(){
+ p.opacity_bubble = p.opacity;
+ p.radius_bubble = p.radius;
+ }
+
+ /* mousemove - check ratio */
+ if(dist_mouse <= pJS.interactivity.modes.bubble.distance){
+
+ if(ratio >= 0 && pJS.interactivity.status == 'mousemove'){
+
+ /* size */
+ if(pJS.interactivity.modes.bubble.size != pJS.particles.size.value){
+
+ if(pJS.interactivity.modes.bubble.size > pJS.particles.size.value){
+ var size = p.radius + (pJS.interactivity.modes.bubble.size*ratio);
+ if(size >= 0){
+ p.radius_bubble = size;
+ }
+ }else{
+ var dif = p.radius - pJS.interactivity.modes.bubble.size,
+ size = p.radius - (dif*ratio);
+ if(size > 0){
+ p.radius_bubble = size;
+ }else{
+ p.radius_bubble = 0;
+ }
+ }
+
+ }
+
+ /* opacity */
+ if(pJS.interactivity.modes.bubble.opacity != pJS.particles.opacity.value){
+
+ if(pJS.interactivity.modes.bubble.opacity > pJS.particles.opacity.value){
+ var opacity = pJS.interactivity.modes.bubble.opacity*ratio;
+ if(opacity > p.opacity && opacity <= pJS.interactivity.modes.bubble.opacity){
+ p.opacity_bubble = opacity;
+ }
+ }else{
+ var opacity = p.opacity - (pJS.particles.opacity.value-pJS.interactivity.modes.bubble.opacity)*ratio;
+ if(opacity < p.opacity && opacity >= pJS.interactivity.modes.bubble.opacity){
+ p.opacity_bubble = opacity;
+ }
+ }
+
+ }
+
+ }
+
+ }else{
+ init();
+ }
+
+
+ /* mouseleave */
+ if(pJS.interactivity.status == 'mouseleave'){
+ init();
+ }
+
+ }
+
+ /* on click event */
+ else if(pJS.interactivity.events.onclick.enable && isInArray('bubble', pJS.interactivity.events.onclick.mode)){
+
+
+ if(pJS.tmp.bubble_clicking){
+ var dx_mouse = p.x - pJS.interactivity.mouse.click_pos_x,
+ dy_mouse = p.y - pJS.interactivity.mouse.click_pos_y,
+ dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse),
+ time_spent = (new Date().getTime() - pJS.interactivity.mouse.click_time)/1000;
+
+ if(time_spent > pJS.interactivity.modes.bubble.duration){
+ pJS.tmp.bubble_duration_end = true;
+ }
+
+ if(time_spent > pJS.interactivity.modes.bubble.duration*2){
+ pJS.tmp.bubble_clicking = false;
+ pJS.tmp.bubble_duration_end = false;
+ }
+ }
+
+
+ function process(bubble_param, particles_param, p_obj_bubble, p_obj, id){
+
+ if(bubble_param != particles_param){
+
+ if(!pJS.tmp.bubble_duration_end){
+ if(dist_mouse <= pJS.interactivity.modes.bubble.distance){
+ if(p_obj_bubble != undefined) var obj = p_obj_bubble;
+ else var obj = p_obj;
+ if(obj != bubble_param){
+ var value = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration);
+ if(id == 'size') p.radius_bubble = value;
+ if(id == 'opacity') p.opacity_bubble = value;
+ }
+ }else{
+ if(id == 'size') p.radius_bubble = undefined;
+ if(id == 'opacity') p.opacity_bubble = undefined;
+ }
+ }else{
+ if(p_obj_bubble != undefined){
+ var value_tmp = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration),
+ dif = bubble_param - value_tmp;
+ value = bubble_param + dif;
+ if(id == 'size') p.radius_bubble = value;
+ if(id == 'opacity') p.opacity_bubble = value;
+ }
+ }
+
+ }
+
+ }
+
+ if(pJS.tmp.bubble_clicking){
+ /* size */
+ process(pJS.interactivity.modes.bubble.size, pJS.particles.size.value, p.radius_bubble, p.radius, 'size');
+ /* opacity */
+ process(pJS.interactivity.modes.bubble.opacity, pJS.particles.opacity.value, p.opacity_bubble, p.opacity, 'opacity');
+ }
+
+ }
+
+ };
+
+
+ pJS.fn.modes.repulseParticle = function(p){
+
+ if(pJS.interactivity.events.onhover.enable && isInArray('repulse', pJS.interactivity.events.onhover.mode) && pJS.interactivity.status == 'mousemove') {
+
+ var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,
+ dy_mouse = p.y - pJS.interactivity.mouse.pos_y,
+ dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse);
+
+ var normVec = {x: dx_mouse/dist_mouse, y: dy_mouse/dist_mouse},
+ repulseRadius = pJS.interactivity.modes.repulse.distance,
+ velocity = 100,
+ repulseFactor = clamp((1/repulseRadius)*(-1*Math.pow(dist_mouse/repulseRadius,2)+1)*repulseRadius*velocity, 0, 50);
+
+ var pos = {
+ x: p.x + normVec.x * repulseFactor,
+ y: p.y + normVec.y * repulseFactor
+ }
+
+ if(pJS.particles.move.out_mode == 'bounce'){
+ if(pos.x - p.radius > 0 && pos.x + p.radius < pJS.canvas.w) p.x = pos.x;
+ if(pos.y - p.radius > 0 && pos.y + p.radius < pJS.canvas.h) p.y = pos.y;
+ }else{
+ p.x = pos.x;
+ p.y = pos.y;
+ }
+
+ }
+
+
+ else if(pJS.interactivity.events.onclick.enable && isInArray('repulse', pJS.interactivity.events.onclick.mode)) {
+
+ if(!pJS.tmp.repulse_finish){
+ pJS.tmp.repulse_count++;
+ if(pJS.tmp.repulse_count == pJS.particles.array.length){
+ pJS.tmp.repulse_finish = true;
+ }
+ }
+
+ if(pJS.tmp.repulse_clicking){
+
+ var repulseRadius = Math.pow(pJS.interactivity.modes.repulse.distance/6, 3);
+
+ var dx = pJS.interactivity.mouse.click_pos_x - p.x,
+ dy = pJS.interactivity.mouse.click_pos_y - p.y,
+ d = dx*dx + dy*dy;
+
+ var force = -repulseRadius / d * 1;
+
+ function process(){
+
+ var f = Math.atan2(dy,dx);
+ p.vx = force * Math.cos(f);
+ p.vy = force * Math.sin(f);
+
+ if(pJS.particles.move.out_mode == 'bounce'){
+ var pos = {
+ x: p.x + p.vx,
+ y: p.y + p.vy
+ }
+ if (pos.x + p.radius > pJS.canvas.w) p.vx = -p.vx;
+ else if (pos.x - p.radius < 0) p.vx = -p.vx;
+ if (pos.y + p.radius > pJS.canvas.h) p.vy = -p.vy;
+ else if (pos.y - p.radius < 0) p.vy = -p.vy;
+ }
+
+ }
+
+ // default
+ if(d <= repulseRadius){
+ process();
+ }
+
+ // bang - slow motion mode
+ // if(!pJS.tmp.repulse_finish){
+ // if(d <= repulseRadius){
+ // process();
+ // }
+ // }else{
+ // process();
+ // }
+
+
+ }else{
+
+ if(pJS.tmp.repulse_clicking == false){
+
+ p.vx = p.vx_i;
+ p.vy = p.vy_i;
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+ pJS.fn.modes.grabParticle = function(p){
+
+ if(pJS.interactivity.events.onhover.enable && pJS.interactivity.status == 'mousemove'){
+
+ var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,
+ dy_mouse = p.y - pJS.interactivity.mouse.pos_y,
+ dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse);
+
+ /* draw a line between the cursor and the particle if the distance between them is under the config distance */
+ if(dist_mouse <= pJS.interactivity.modes.grab.distance){
+
+ 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;
+
+ if(opacity_line > 0){
+
+ /* style */
+ var color_line = pJS.particles.line_linked.color_rgb_line;
+ pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')';
+ pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width;
+ //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */
+
+ /* path */
+ pJS.canvas.ctx.beginPath();
+ pJS.canvas.ctx.moveTo(p.x, p.y);
+ pJS.canvas.ctx.lineTo(pJS.interactivity.mouse.pos_x, pJS.interactivity.mouse.pos_y);
+ pJS.canvas.ctx.stroke();
+ pJS.canvas.ctx.closePath();
+
+ }
+
+ }
+
+ }
+
+ };
+
+
+
+ /* ---------- pJS functions - vendors ------------ */
+
+ pJS.fn.vendors.eventsListeners = function(){
+
+ /* events target element */
+ if(pJS.interactivity.detect_on == 'window'){
+ pJS.interactivity.el = window;
+ }else{
+ pJS.interactivity.el = pJS.canvas.el;
+ }
+
+
+ /* detect mouse pos - on hover / click event */
+ if(pJS.interactivity.events.onhover.enable || pJS.interactivity.events.onclick.enable){
+
+ /* el on mousemove */
+ pJS.interactivity.el.addEventListener('mousemove', function(e){
+
+ if(pJS.interactivity.el == window){
+ var pos_x = e.clientX,
+ pos_y = e.clientY;
+ }
+ else{
+ var pos_x = e.offsetX || e.clientX,
+ pos_y = e.offsetY || e.clientY;
+ }
+
+ pJS.interactivity.mouse.pos_x = pos_x;
+ pJS.interactivity.mouse.pos_y = pos_y;
+
+ if(pJS.tmp.retina){
+ pJS.interactivity.mouse.pos_x *= pJS.canvas.pxratio;
+ pJS.interactivity.mouse.pos_y *= pJS.canvas.pxratio;
+ }
+
+ pJS.interactivity.status = 'mousemove';
+
+ });
+
+ /* el on onmouseleave */
+ pJS.interactivity.el.addEventListener('mouseleave', function(e){
+
+ pJS.interactivity.mouse.pos_x = null;
+ pJS.interactivity.mouse.pos_y = null;
+ pJS.interactivity.status = 'mouseleave';
+
+ });
+
+ }
+
+ /* on click event */
+ if(pJS.interactivity.events.onclick.enable){
+
+ pJS.interactivity.el.addEventListener('click', function(){
+
+ pJS.interactivity.mouse.click_pos_x = pJS.interactivity.mouse.pos_x;
+ pJS.interactivity.mouse.click_pos_y = pJS.interactivity.mouse.pos_y;
+ pJS.interactivity.mouse.click_time = new Date().getTime();
+
+ if(pJS.interactivity.events.onclick.enable){
+
+ switch(pJS.interactivity.events.onclick.mode){
+
+ case 'push':
+ if(pJS.particles.move.enable){
+ pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse);
+ }else{
+ if(pJS.interactivity.modes.push.particles_nb == 1){
+ pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse);
+ }
+ else if(pJS.interactivity.modes.push.particles_nb > 1){
+ pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb);
+ }
+ }
+ break;
+
+ case 'remove':
+ pJS.fn.modes.removeParticles(pJS.interactivity.modes.remove.particles_nb);
+ break;
+
+ case 'bubble':
+ pJS.tmp.bubble_clicking = true;
+ break;
+
+ case 'repulse':
+ pJS.tmp.repulse_clicking = true;
+ pJS.tmp.repulse_count = 0;
+ pJS.tmp.repulse_finish = false;
+ setTimeout(function(){
+ pJS.tmp.repulse_clicking = false;
+ }, pJS.interactivity.modes.repulse.duration*1000)
+ break;
+
+ }
+
+ }
+
+ });
+
+ }
+
+
+ };
+
+ pJS.fn.vendors.densityAutoParticles = function(){
+
+ if(pJS.particles.number.density.enable){
+
+ /* calc area */
+ var area = pJS.canvas.el.width * pJS.canvas.el.height / 1000;
+ if(pJS.tmp.retina){
+ area = area/(pJS.canvas.pxratio*2);
+ }
+
+ /* calc number of particles based on density area */
+ var nb_particles = area * pJS.particles.number.value / pJS.particles.number.density.value_area;
+
+ /* add or remove X particles */
+ var missing_particles = pJS.particles.array.length - nb_particles;
+ if(missing_particles < 0) pJS.fn.modes.pushParticles(Math.abs(missing_particles));
+ else pJS.fn.modes.removeParticles(missing_particles);
+
+ }
+
+ };
+
+
+ pJS.fn.vendors.checkOverlap = function(p1, position){
+ for(var i = 0; i < pJS.particles.array.length; i++){
+ var p2 = pJS.particles.array[i];
+
+ var dx = p1.x - p2.x,
+ dy = p1.y - p2.y,
+ dist = Math.sqrt(dx*dx + dy*dy);
+
+ if(dist <= p1.radius + p2.radius){
+ p1.x = position ? position.x : Math.random() * pJS.canvas.w;
+ p1.y = position ? position.y : Math.random() * pJS.canvas.h;
+ pJS.fn.vendors.checkOverlap(p1);
+ }
+ }
+ };
+
+
+ pJS.fn.vendors.createSvgImg = function(p){
+
+ /* set color to svg element */
+ var svgXml = pJS.tmp.source_svg,
+ rgbHex = /#([0-9A-F]{3,6})/gi,
+ coloredSvgXml = svgXml.replace(rgbHex, function (m, r, g, b) {
+ if(p.color.rgb){
+ var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+p.opacity+')';
+ }else{
+ var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+p.opacity+')';
+ }
+ return color_value;
+ });
+
+ /* prepare to create img with colored svg */
+ var svg = new Blob([coloredSvgXml], {type: 'image/svg+xml;charset=utf-8'}),
+ DOMURL = window.URL || window.webkitURL || window,
+ url = DOMURL.createObjectURL(svg);
+
+ /* create particle img obj */
+ var img = new Image();
+ img.addEventListener('load', function(){
+ p.img.obj = img;
+ p.img.loaded = true;
+ DOMURL.revokeObjectURL(url);
+ pJS.tmp.count_svg++;
+ });
+ img.src = url;
+
+ };
+
+
+ pJS.fn.vendors.destroypJS = function(){
+ cancelAnimationFrame(pJS.fn.drawAnimFrame);
+ canvas_el.remove();
+ pJSDom = null;
+ };
+
+
+ pJS.fn.vendors.drawShape = function(c, startX, startY, sideLength, sideCountNumerator, sideCountDenominator){
+
+ // By Programming Thomas - https://programmingthomas.wordpress.com/2013/04/03/n-sided-shapes/
+ var sideCount = sideCountNumerator * sideCountDenominator;
+ var decimalSides = sideCountNumerator / sideCountDenominator;
+ var interiorAngleDegrees = (180 * (decimalSides - 2)) / decimalSides;
+ var interiorAngle = Math.PI - Math.PI * interiorAngleDegrees / 180; // convert to radians
+ c.save();
+ c.beginPath();
+ c.translate(startX, startY);
+ c.moveTo(0,0);
+ for (var i = 0; i < sideCount; i++) {
+ c.lineTo(sideLength,0);
+ c.translate(sideLength,0);
+ c.rotate(interiorAngle);
+ }
+ //c.stroke();
+ c.fill();
+ c.restore();
+
+ };
+
+ pJS.fn.vendors.exportImg = function(){
+ window.open(pJS.canvas.el.toDataURL('image/png'), '_blank');
+ };
+
+
+ pJS.fn.vendors.loadImg = function(type){
+
+ pJS.tmp.img_error = undefined;
+
+ if(pJS.particles.shape.image.src != ''){
+
+ if(type == 'svg'){
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', pJS.particles.shape.image.src);
+ xhr.onreadystatechange = function (data) {
+ if(xhr.readyState == 4){
+ if(xhr.status == 200){
+ pJS.tmp.source_svg = data.currentTarget.response;
+ pJS.fn.vendors.checkBeforeDraw();
+ }else{
+ console.log('Error pJS - Image not found');
+ pJS.tmp.img_error = true;
+ }
+ }
+ }
+ xhr.send();
+
+ }else{
+
+ var img = new Image();
+ img.addEventListener('load', function(){
+ pJS.tmp.img_obj = img;
+ pJS.fn.vendors.checkBeforeDraw();
+ });
+ img.src = pJS.particles.shape.image.src;
+
+ }
+
+ }else{
+ console.log('Error pJS - No image.src');
+ pJS.tmp.img_error = true;
+ }
+
+ };
+
+
+ pJS.fn.vendors.draw = function(){
+
+ if(pJS.particles.shape.type == 'image'){
+
+ if(pJS.tmp.img_type == 'svg'){
+
+ if(pJS.tmp.count_svg >= pJS.particles.number.value){
+ pJS.fn.particlesDraw();
+ if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);
+ else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
+ }else{
+ //console.log('still loading...');
+ if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
+ }
+
+ }else{
+
+ if(pJS.tmp.img_obj != undefined){
+ pJS.fn.particlesDraw();
+ if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);
+ else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
+ }else{
+ if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
+ }
+
+ }
+
+ }else{
+ pJS.fn.particlesDraw();
+ if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);
+ else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
+ }
+
+ };
+
+
+ pJS.fn.vendors.checkBeforeDraw = function(){
+
+ // if shape is image
+ if(pJS.particles.shape.type == 'image'){
+
+ if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg == undefined){
+ pJS.tmp.checkAnimFrame = requestAnimFrame(check);
+ }else{
+ //console.log('images loaded! cancel check');
+ cancelRequestAnimFrame(pJS.tmp.checkAnimFrame);
+ if(!pJS.tmp.img_error){
+ pJS.fn.vendors.init();
+ pJS.fn.vendors.draw();
+ }
+
+ }
+
+ }else{
+ pJS.fn.vendors.init();
+ pJS.fn.vendors.draw();
+ }
+
+ };
+
+
+ pJS.fn.vendors.init = function(){
+
+ /* init canvas + particles */
+ pJS.fn.retinaInit();
+ pJS.fn.canvasInit();
+ pJS.fn.canvasSize();
+ pJS.fn.canvasPaint();
+ pJS.fn.particlesCreate();
+ pJS.fn.vendors.densityAutoParticles();
+
+ /* particles.line_linked - convert hex colors to rgb */
+ pJS.particles.line_linked.color_rgb_line = hexToRgb(pJS.particles.line_linked.color);
+
+ };
+
+
+ pJS.fn.vendors.start = function(){
+
+ if(isInArray('image', pJS.particles.shape.type)){
+ pJS.tmp.img_type = pJS.particles.shape.image.src.substr(pJS.particles.shape.image.src.length - 3);
+ pJS.fn.vendors.loadImg(pJS.tmp.img_type);
+ }else{
+ pJS.fn.vendors.checkBeforeDraw();
+ }
+
+ };
+
+
+
+
+ /* ---------- pJS - start ------------ */
+
+
+ pJS.fn.vendors.eventsListeners();
+
+ pJS.fn.vendors.start();
+
+
+
+};
+
+/* ---------- global functions - vendors ------------ */
+
+Object.deepExtend = function(destination, source) {
+ for (var property in source) {
+ if (source[property] && source[property].constructor &&
+ source[property].constructor === Object) {
+ destination[property] = destination[property] || {};
+ Object.deepExtend(destination[property], source[property]);
+ } else {
+ destination[property] = source[property];
+ }
+ }
+ return destination;
+};
+
+window.requestAnimFrame = (function(){
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback){
+ window.setTimeout(callback, 1000 / 60);
+ };
+})();
+
+window.cancelRequestAnimFrame = ( function() {
+ return window.cancelAnimationFrame ||
+ window.webkitCancelRequestAnimationFrame ||
+ window.mozCancelRequestAnimationFrame ||
+ window.oCancelRequestAnimationFrame ||
+ window.msCancelRequestAnimationFrame ||
+ clearTimeout
+} )();
+
+function hexToRgb(hex){
+ // By Tim Down - http://stackoverflow.com/a/5624139/3493650
+ // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
+ var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
+ hex = hex.replace(shorthandRegex, function(m, r, g, b) {
+ return r + r + g + g + b + b;
+ });
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16)
+ } : null;
+};
+
+function clamp(number, min, max) {
+ return Math.min(Math.max(number, min), max);
+};
+
+function isInArray(value, array) {
+ return array.indexOf(value) > -1;
+}
+
+
+/* ---------- particles.js functions - start ------------ */
+
+window.pJSDom = [];
+
+window.particlesJS = function(tag_id, params){
+ if (!document) {
+ return;
+ }
+ //console.log(params);
+
+ /* no string id? so it's object params, and set the id with default id */
+ if(typeof(tag_id) != 'string'){
+ params = tag_id;
+ tag_id = 'particles-js';
+ }
+
+ /* no id? set the id to default id */
+ if(!tag_id){
+ tag_id = 'particles-js';
+ }
+
+ /* pJS elements */
+ var pJS_tag = document.getElementById(tag_id),
+ pJS_canvas_class = 'particles-js-canvas-el',
+ exist_canvas = pJS_tag.getElementsByClassName(pJS_canvas_class);
+
+ /* remove canvas if exists into the pJS target tag */
+ if(exist_canvas.length){
+ while(exist_canvas.length > 0){
+ pJS_tag.removeChild(exist_canvas[0]);
+ }
+ }
+
+ /* create canvas element */
+ var canvas_el = document.createElement('canvas');
+ canvas_el.className = pJS_canvas_class;
+
+ /* set size canvas */
+ canvas_el.style.width = "100%";
+ canvas_el.style.height = "100%";
+
+ /* append canvas */
+ var canvas = document.getElementById(tag_id).appendChild(canvas_el);
+
+ /* launch particle.js */
+ if(canvas != null){
+ pJSDom.push(new pJS(tag_id, params));
+ }
+
+};
+
+window.particlesJS.load = function(tag_id, path_config_json, callback){
+
+ /* load json config */
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', path_config_json);
+ xhr.onreadystatechange = function (data) {
+ if(xhr.readyState == 4){
+ if(xhr.status == 200){
+ var params = JSON.parse(data.currentTarget.response);
+ window.particlesJS(tag_id, params);
+ if(callback) callback();
+ }else{
+ console.log('Error pJS - XMLHttpRequest status: '+xhr.status);
+ console.log('Error pJS - File config not found');
+ }
+ }
+ };
+ xhr.send();
+
+};
+
+module.exports = window.particlesJS;
diff --git a/docs/wrappers/html.js b/docs/wrappers/html.js
new file mode 100644
index 00000000..da86a154
--- /dev/null
+++ b/docs/wrappers/html.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import DocumentTitle from 'react-document-title';
+
+class HTML extends React.Component {
+ render() {
+ var page = this.props.page.data;
+ return (
+
+ );
+ }
+}
+
+module.exports = HTML;
diff --git a/docs/wrappers/md.js b/docs/wrappers/md.js
new file mode 100644
index 00000000..ddde3df8
--- /dev/null
+++ b/docs/wrappers/md.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import DocumentTitle from 'react-document-title';
+import { link } from 'gatsby-helpers';
+
+var DOCS_BASEURL = "https://github.com/graphql-python/graphene/edit/master/docs/pages/";
+
+class Markdown extends React.Component {
+ render() {
+ var post = this.props.page.data;
+ var pagePath = this.props.page.requirePath;
+ var documentUrl = `${DOCS_BASEURL}${pagePath}`;
+
+ return (
+
+
+
+ );
+ }
+}
+
+module.exports = Markdown;