├── README.md ├── .gitignore ├── requirements.txt ├── static ├── 1.png ├── 10.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png ├── 9.png ├── 404.css ├── style.css ├── app.js └── particles.js ├── Procfile ├── templates ├── confirm.html ├── landing.html ├── index.html └── 404.html └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # GDSC -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | gunicorn -------------------------------------------------------------------------------- /static/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/1.png -------------------------------------------------------------------------------- /static/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/10.png -------------------------------------------------------------------------------- /static/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/2.png -------------------------------------------------------------------------------- /static/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/3.png -------------------------------------------------------------------------------- /static/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/4.png -------------------------------------------------------------------------------- /static/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/5.png -------------------------------------------------------------------------------- /static/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/6.png -------------------------------------------------------------------------------- /static/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/7.png -------------------------------------------------------------------------------- /static/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/8.png -------------------------------------------------------------------------------- /static/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xditya/Bifrost/main/static/9.png -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | # Modify this Procfile to fit your needs 2 | web: gunicorn main:app 3 | -------------------------------------------------------------------------------- /templates/confirm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | Bifrost Inc. 8 | 9 | 10 |

12 |
13 |
Bifrost Inc.

14 |

15 | Please confirm your booking for {{portal_id}}
16 |

17 | 18 | 19 |

20 | 21 | -------------------------------------------------------------------------------- /static/404.css: -------------------------------------------------------------------------------- 1 | /* https://codepen.io/namratapdr/pen/yLOgREo */ 2 | 3 | @import url('https://fonts.googleapis.com/css2?family=Permanent+Marker&family=Fira+Mono:wght@500&display=swap'); 4 | 5 | body { 6 | height: 95vh; 7 | background: #000000; 8 | text-align: center; 9 | color: #E0E0E0; 10 | font-family: 'Fira Mono', monospace; 11 | } 12 | 13 | h1 { 14 | font-size: 2.5rem; 15 | font-family: 'Permanent Marker', cursive; 16 | } 17 | 18 | div { 19 | transform-style: preserve-3d; 20 | } 21 | 22 | svg { 23 | width: clamp(300px, 70%, 600px); 24 | height: 500px; 25 | 26 | } 27 | 28 | #rocket { 29 | 30 | transform: translateY(750px); 31 | 32 | animation: launch 2s ease-out forwards; 33 | } 34 | 35 | @keyframes launch { 36 | from { 37 | transform: translateY(750px); 38 | } 39 | 40 | to { 41 | perspective: 500px; 42 | transform: translateY(0px); 43 | } 44 | } 45 | 46 | #stars { 47 | animation: twinkling 2s linear; 48 | } 49 | 50 | @keyframes twinkling { 51 | 52 | from { 53 | transform: scale(0); 54 | } 55 | 56 | to { 57 | transform: scale(1); 58 | } 59 | } 60 | 61 | .text { 62 | opacity: 0; 63 | animation: appear 1s ease-in forwards; 64 | animation-delay: 1.8s; 65 | } 66 | 67 | @keyframes appear { 68 | from { 69 | opacity: 0; 70 | } 71 | 72 | to { 73 | opacity: 1; 74 | } 75 | } 76 | 77 | a { 78 | color: #F66947; 79 | text-decoration: none; 80 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import string 3 | from random import choice 4 | 5 | from flask import Flask, render_template, request, redirect 6 | 7 | app = Flask(__name__, static_folder="static") 8 | 9 | 10 | ips = {} 11 | 12 | 13 | @app.route("/") 14 | def home_page(): 15 | ip = request.environ["REMOTE_ADDR"] 16 | # print("IP:", ip) 17 | return portal_clicked() if ip in ips else render_template("index.html") 18 | 19 | 20 | @app.route("/confirm") 21 | def confirm(): 22 | portal_number = request.args.get("id") 23 | return ( 24 | render_template("confirm.html", portal_id=portal_number) 25 | if portal_number 26 | else render_template("404.html") 27 | ) 28 | 29 | 30 | @app.route("/portal") 31 | def portal_clicked(): 32 | portal_number = request.args.get("id") 33 | if not portal_number: 34 | return render_template("404.html") 35 | ip = request.remote_addr 36 | random_booking_id = "".join( 37 | [choice(string.ascii_letters + string.digits) for _ in range(10)] 38 | ) 39 | if ip not in list(ips.keys()): 40 | ips[ip] = random_booking_id 41 | return render_template( 42 | "landing.html", booking_id=random_booking_id, portal_number=portal_number 43 | ) 44 | 45 | 46 | @app.route("/cancel") 47 | def canceller(): 48 | portal_number = request.args.get("id") 49 | if not portal_number: 50 | return render_template("404.html") 51 | ip = request.remote_addr 52 | if ip in list(ips.keys()): 53 | ips.pop(ip) 54 | return redirect("/") 55 | 56 | 57 | # app.run(debug=True, port=5000) 58 | # app.run(debug=True, port=8080) 59 | -------------------------------------------------------------------------------- /templates/landing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bifrost Inc. 6 | 8 | Bifrost Inc. 9 | 10 | 11 |
12 |
13 |

15 |
16 |
Bifrost Inc.

17 |

18 | Thank you for your purchase!
19 | Your Booking ID: {{booking_id}}

20 | Portal Number: {{portal_number}}

21 | 22 | 23 | 24 | 25 |

26 | 27 |

28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 1 | .card { 2 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); 3 | max-width: 300px; 4 | width: auto; 5 | margin: auto; 6 | text-align: center; 7 | font-family: arial; 8 | background-color: azure; 9 | } 10 | 11 | .price { 12 | color: grey; 13 | font-size: 22px; 14 | } 15 | 16 | .card button { 17 | border: none; 18 | outline: 0; 19 | padding: 12px; 20 | color: white; 21 | background-color: #000; 22 | text-align: center; 23 | cursor: pointer; 24 | width: 100%; 25 | font-size: 18px; 26 | } 27 | 28 | .card button:hover { 29 | opacity: 0.7; 30 | } 31 | 32 | #h { 33 | color: white; 34 | text-align: center; 35 | } 36 | 37 | body { 38 | background-color: #040119; 39 | } 40 | 41 | table { 42 | align-self: center; 43 | padding-top: 5px; 44 | padding-bottom: 5px; 45 | padding-right: 10%; 46 | padding-left: 10%; 47 | } 48 | 49 | tr { 50 | padding-top: 5px; 51 | width: auto; 52 | height: max-content; 53 | } 54 | 55 | td { 56 | padding-right: 10px; 57 | padding-left: 10px; 58 | height: inherit; 59 | width: 20%; 60 | } 61 | 62 | #p { 63 | align-self: center; 64 | text-align: center; 65 | color: blanchedalmond; 66 | } 67 | 68 | img { 69 | width: auto; 70 | align-self: center; 71 | } 72 | 73 | button { 74 | align-self: center; 75 | } 76 | 77 | body { 78 | width: 100%; 79 | } 80 | 81 | #land { 82 | align-self: center; 83 | text-align: center; 84 | 85 | } 86 | 87 | body, 88 | html { 89 | height: 100% 90 | } 91 | 92 | #particles-js canvas { 93 | display: block; 94 | vertical-align: bottom; 95 | -webkit-transform: scale(1); 96 | -ms-transform: scale(1); 97 | transform: scale(1); 98 | opacity: 1; 99 | -webkit-transition: opacity .8s ease, -webkit-transform 1.4s ease; 100 | transition: opacity .8s ease, transform 1.4s ease 101 | } 102 | 103 | #particles-js { 104 | width: 100%; 105 | height: 100%; 106 | position: fixed; 107 | z-index: -10; 108 | top: 0; 109 | left: 0 110 | } 111 | 112 | canvas { 113 | display: block; 114 | position: fixed; 115 | z-index: -1; 116 | } -------------------------------------------------------------------------------- /static/app.js: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------- 2 | /* How to use? : Check the GitHub README 3 | /* ----------------------------------------------- */ 4 | 5 | /* To load a config file (particles.json) you need to host this demo (MAMP/WAMP/local)... */ 6 | /* 7 | particlesJS.load('particles-js', 'particles.json', function() { 8 | console.log('particles.js loaded - callback'); 9 | }); 10 | */ 11 | 12 | /* Otherwise just put the config content (json): */ 13 | 14 | particlesJS("particles-js", { 15 | particles: { 16 | number: { 17 | value: 360, 18 | density: { 19 | enable: true, 20 | value_area: 800, 21 | }, 22 | }, 23 | color: { 24 | value: "#ffffff", 25 | }, 26 | shape: { 27 | type: "star", 28 | stroke: { 29 | width: 0, 30 | color: "#000000", 31 | }, 32 | polygon: { 33 | nb_sides: 5, 34 | }, 35 | image: { 36 | src: "img/github.svg", 37 | width: 100, 38 | height: 100, 39 | }, 40 | }, 41 | opacity: { 42 | value: 1, 43 | random: true, 44 | anim: { 45 | enable: true, 46 | speed: 1, 47 | opacity_min: 0, 48 | sync: false, 49 | }, 50 | }, 51 | size: { 52 | value: 3, 53 | random: true, 54 | anim: { 55 | enable: false, 56 | speed: 4, 57 | size_min: 0.3, 58 | sync: false, 59 | }, 60 | }, 61 | line_linked: { 62 | enable: false, 63 | distance: 150, 64 | color: "#ffffff", 65 | opacity: 0.4, 66 | width: 1, 67 | }, 68 | move: { 69 | enable: true, 70 | speed: 1, 71 | direction: "none", 72 | random: true, 73 | straight: false, 74 | out_mode: "out", 75 | bounce: false, 76 | attract: { 77 | enable: false, 78 | rotateX: 600, 79 | rotateY: 600, 80 | }, 81 | }, 82 | }, 83 | interactivity: { 84 | detect_on: "canvas", 85 | events: { 86 | onhover: { 87 | enable: true, 88 | mode: "bubble", 89 | }, 90 | onclick: { 91 | enable: true, 92 | mode: "repulse", 93 | }, 94 | resize: true, 95 | }, 96 | modes: { 97 | grab: { 98 | distance: 400, 99 | line_linked: { 100 | opacity: 1, 101 | }, 102 | }, 103 | bubble: { 104 | distance: 250, 105 | size: 0, 106 | duration: 2, 107 | opacity: 0, 108 | speed: 3, 109 | }, 110 | repulse: { 111 | distance: 400, 112 | duration: 0.4, 113 | }, 114 | push: { 115 | particles_nb: 4, 116 | }, 117 | remove: { 118 | particles_nb: 2, 119 | }, 120 | }, 121 | }, 122 | retina_detect: true, 123 | }); 124 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | Bifrost Inc. 8 | 9 | 10 |
11 |
12 |

14 |
17 |
Bifrost Inc.

18 |

Current Location: Earth

19 | 20 | 21 | 30 | 40 | 48 | 57 | 66 | 67 | 76 | 86 | 94 | 103 | 112 |
22 | Portal 1 23 |

Hydrargyrum

24 |

58,999

25 |

Recommended for the unhinged.
Preference to Diamond skinned 26 | races.

27 |

29 |
31 |
32 | Portal 2 33 |

Daenerys

34 |

779,999

35 |

Her children really do want this planet.
Don't you want it 36 | too?

37 |

39 |
41 | Portal 3 42 |

Wine

43 |

96,999

44 |

Need I say more?

45 |

47 |
49 | Portal 4 50 |

Medovyy

51 |

155,999

52 |

Meet the darling dear members of the Winnie De Pooh 53 | species.

54 |

56 |
58 | Portal 5 59 |

Omu Lette

60 |

125,999

61 |

Some say there lies a dragon child
sleeping at the core.
Too 62 | bad it's so close to it's sun.

63 |

65 |
68 | Portal 6 69 |

Earthalla

70 |

99,999

71 |

The dear long lost twin of our very own Earth.
Separated by 72 | the big bang and conjoint now by
Bifrost Inc.

73 |

75 |
77 | Portal 7 78 |

Amoongus

79 |

69,899

80 |

Scientists have searched long and hard, yet no one knows which 81 | of 82 | the two references the name is based on.

83 |

85 |
87 | Portal 8 88 |

Reality

89 |

49,999

90 |

Some say, often disappointing.

91 |

93 |
95 | Portal 9 96 |

Snice

97 |

98,999

98 |

Is it ice? Is it snow? No one knows but don't miss those! Just 99 | don't light a fire over here.

100 |

102 |
104 | Portal 10 105 |

Licorie

106 |

88,999

107 |

Green and lush. Almost as if the name refers to something 108 | similar.
Reminder: Hate the food, not the planet!

109 |

111 |
113 |
114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /static/particles.js: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------- 2 | /* Author : Vincent Garreau - vincentgarreau.com 3 | /* MIT license: http://opensource.org/licenses/MIT 4 | /* Demo / Generator : vincentgarreau.com/particles.js 5 | /* GitHub : github.com/VincentGarreau/particles.js 6 | /* How to use? : Check the GitHub README 7 | /* v2.0.0 8 | /* ----------------------------------------------- */ 9 | 10 | var pJS = function (tag_id, params) { 11 | var canvas_el = document.querySelector( 12 | "#" + tag_id + " > .particles-js-canvas-el" 13 | ); 14 | 15 | /* particles.js variables with default values */ 16 | this.pJS = { 17 | canvas: { 18 | el: canvas_el, 19 | w: canvas_el.offsetWidth, 20 | h: canvas_el.offsetHeight, 21 | }, 22 | particles: { 23 | number: { 24 | value: 400, 25 | density: { 26 | enable: true, 27 | value_area: 800, 28 | }, 29 | }, 30 | color: { 31 | value: "#fff", 32 | }, 33 | shape: { 34 | type: "circle", 35 | stroke: { 36 | width: 0, 37 | color: "#ff0000", 38 | }, 39 | polygon: { 40 | nb_sides: 5, 41 | }, 42 | image: { 43 | src: "", 44 | width: 100, 45 | height: 100, 46 | }, 47 | }, 48 | opacity: { 49 | value: 1, 50 | random: false, 51 | anim: { 52 | enable: false, 53 | speed: 2, 54 | opacity_min: 0, 55 | sync: false, 56 | }, 57 | }, 58 | size: { 59 | value: 20, 60 | random: false, 61 | anim: { 62 | enable: false, 63 | speed: 20, 64 | size_min: 0, 65 | sync: false, 66 | }, 67 | }, 68 | line_linked: { 69 | enable: true, 70 | distance: 100, 71 | color: "#fff", 72 | opacity: 1, 73 | width: 1, 74 | }, 75 | move: { 76 | enable: true, 77 | speed: 2, 78 | direction: "none", 79 | random: false, 80 | straight: false, 81 | out_mode: "out", 82 | bounce: false, 83 | attract: { 84 | enable: false, 85 | rotateX: 3000, 86 | rotateY: 3000, 87 | }, 88 | }, 89 | array: [], 90 | }, 91 | interactivity: { 92 | detect_on: "canvas", 93 | events: { 94 | onhover: { 95 | enable: true, 96 | mode: "grab", 97 | }, 98 | onclick: { 99 | enable: true, 100 | mode: "push", 101 | }, 102 | resize: true, 103 | }, 104 | modes: { 105 | grab: { 106 | distance: 100, 107 | line_linked: { 108 | opacity: 1, 109 | }, 110 | }, 111 | bubble: { 112 | distance: 200, 113 | size: 80, 114 | duration: 0.4, 115 | }, 116 | repulse: { 117 | distance: 200, 118 | duration: 0.4, 119 | }, 120 | push: { 121 | particles_nb: 4, 122 | }, 123 | remove: { 124 | particles_nb: 2, 125 | }, 126 | }, 127 | mouse: {}, 128 | }, 129 | retina_detect: false, 130 | fn: { 131 | interact: {}, 132 | modes: {}, 133 | vendors: {}, 134 | }, 135 | tmp: {}, 136 | }; 137 | 138 | var pJS = this.pJS; 139 | 140 | /* params settings */ 141 | if (params) { 142 | Object.deepExtend(pJS, params); 143 | } 144 | 145 | pJS.tmp.obj = { 146 | size_value: pJS.particles.size.value, 147 | size_anim_speed: pJS.particles.size.anim.speed, 148 | move_speed: pJS.particles.move.speed, 149 | line_linked_distance: pJS.particles.line_linked.distance, 150 | line_linked_width: pJS.particles.line_linked.width, 151 | mode_grab_distance: pJS.interactivity.modes.grab.distance, 152 | mode_bubble_distance: pJS.interactivity.modes.bubble.distance, 153 | mode_bubble_size: pJS.interactivity.modes.bubble.size, 154 | mode_repulse_distance: pJS.interactivity.modes.repulse.distance, 155 | }; 156 | 157 | pJS.fn.retinaInit = function () { 158 | if (pJS.retina_detect && window.devicePixelRatio > 1) { 159 | pJS.canvas.pxratio = window.devicePixelRatio; 160 | pJS.tmp.retina = true; 161 | } else { 162 | pJS.canvas.pxratio = 1; 163 | pJS.tmp.retina = false; 164 | } 165 | 166 | pJS.canvas.w = pJS.canvas.el.offsetWidth * pJS.canvas.pxratio; 167 | pJS.canvas.h = pJS.canvas.el.offsetHeight * pJS.canvas.pxratio; 168 | 169 | pJS.particles.size.value = pJS.tmp.obj.size_value * pJS.canvas.pxratio; 170 | pJS.particles.size.anim.speed = 171 | pJS.tmp.obj.size_anim_speed * pJS.canvas.pxratio; 172 | pJS.particles.move.speed = pJS.tmp.obj.move_speed * pJS.canvas.pxratio; 173 | pJS.particles.line_linked.distance = 174 | pJS.tmp.obj.line_linked_distance * pJS.canvas.pxratio; 175 | pJS.interactivity.modes.grab.distance = 176 | pJS.tmp.obj.mode_grab_distance * pJS.canvas.pxratio; 177 | pJS.interactivity.modes.bubble.distance = 178 | pJS.tmp.obj.mode_bubble_distance * pJS.canvas.pxratio; 179 | pJS.particles.line_linked.width = 180 | pJS.tmp.obj.line_linked_width * pJS.canvas.pxratio; 181 | pJS.interactivity.modes.bubble.size = 182 | pJS.tmp.obj.mode_bubble_size * pJS.canvas.pxratio; 183 | pJS.interactivity.modes.repulse.distance = 184 | pJS.tmp.obj.mode_repulse_distance * pJS.canvas.pxratio; 185 | }; 186 | 187 | /* ---------- pJS functions - canvas ------------ */ 188 | 189 | pJS.fn.canvasInit = function () { 190 | pJS.canvas.ctx = pJS.canvas.el.getContext("2d"); 191 | }; 192 | 193 | pJS.fn.canvasSize = function () { 194 | pJS.canvas.el.width = pJS.canvas.w; 195 | pJS.canvas.el.height = pJS.canvas.h; 196 | 197 | if (pJS && pJS.interactivity.events.resize) { 198 | window.addEventListener("resize", function () { 199 | pJS.canvas.w = pJS.canvas.el.offsetWidth; 200 | pJS.canvas.h = pJS.canvas.el.offsetHeight; 201 | 202 | /* resize canvas */ 203 | if (pJS.tmp.retina) { 204 | pJS.canvas.w *= pJS.canvas.pxratio; 205 | pJS.canvas.h *= pJS.canvas.pxratio; 206 | } 207 | 208 | pJS.canvas.el.width = pJS.canvas.w; 209 | pJS.canvas.el.height = pJS.canvas.h; 210 | 211 | /* repaint canvas on anim disabled */ 212 | if (!pJS.particles.move.enable) { 213 | pJS.fn.particlesEmpty(); 214 | pJS.fn.particlesCreate(); 215 | pJS.fn.particlesDraw(); 216 | pJS.fn.vendors.densityAutoParticles(); 217 | } 218 | 219 | /* density particles enabled */ 220 | pJS.fn.vendors.densityAutoParticles(); 221 | }); 222 | } 223 | }; 224 | 225 | pJS.fn.canvasPaint = function () { 226 | pJS.canvas.ctx.fillRect(0, 0, pJS.canvas.w, pJS.canvas.h); 227 | }; 228 | 229 | pJS.fn.canvasClear = function () { 230 | pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h); 231 | }; 232 | 233 | /* --------- pJS functions - particles ----------- */ 234 | 235 | pJS.fn.particle = function (color, opacity, position) { 236 | /* size */ 237 | this.radius = 238 | (pJS.particles.size.random ? Math.random() : 1) * 239 | pJS.particles.size.value; 240 | if (pJS.particles.size.anim.enable) { 241 | this.size_status = false; 242 | this.vs = pJS.particles.size.anim.speed / 100; 243 | if (!pJS.particles.size.anim.sync) { 244 | this.vs = this.vs * Math.random(); 245 | } 246 | } 247 | 248 | /* position */ 249 | this.x = position ? position.x : Math.random() * pJS.canvas.w; 250 | this.y = position ? position.y : Math.random() * pJS.canvas.h; 251 | 252 | /* check position - into the canvas */ 253 | if (this.x > pJS.canvas.w - this.radius * 2) this.x = this.x - this.radius; 254 | else if (this.x < this.radius * 2) this.x = this.x + this.radius; 255 | if (this.y > pJS.canvas.h - this.radius * 2) this.y = this.y - this.radius; 256 | else if (this.y < this.radius * 2) this.y = this.y + this.radius; 257 | 258 | /* check position - avoid overlap */ 259 | if (pJS.particles.move.bounce) { 260 | pJS.fn.vendors.checkOverlap(this, position); 261 | } 262 | 263 | /* color */ 264 | this.color = {}; 265 | if (typeof color.value == "object") { 266 | if (color.value instanceof Array) { 267 | var color_selected = 268 | color.value[ 269 | Math.floor(Math.random() * pJS.particles.color.value.length) 270 | ]; 271 | this.color.rgb = hexToRgb(color_selected); 272 | } else { 273 | if ( 274 | color.value.r != undefined && 275 | color.value.g != undefined && 276 | color.value.b != undefined 277 | ) { 278 | this.color.rgb = { 279 | r: color.value.r, 280 | g: color.value.g, 281 | b: color.value.b, 282 | }; 283 | } 284 | if ( 285 | color.value.h != undefined && 286 | color.value.s != undefined && 287 | color.value.l != undefined 288 | ) { 289 | this.color.hsl = { 290 | h: color.value.h, 291 | s: color.value.s, 292 | l: color.value.l, 293 | }; 294 | } 295 | } 296 | } else if (color.value == "random") { 297 | this.color.rgb = { 298 | r: Math.floor(Math.random() * (255 - 0 + 1)) + 0, 299 | g: Math.floor(Math.random() * (255 - 0 + 1)) + 0, 300 | b: Math.floor(Math.random() * (255 - 0 + 1)) + 0, 301 | }; 302 | } else if (typeof color.value == "string") { 303 | this.color = color; 304 | this.color.rgb = hexToRgb(this.color.value); 305 | } 306 | 307 | /* opacity */ 308 | this.opacity = 309 | (pJS.particles.opacity.random ? Math.random() : 1) * 310 | pJS.particles.opacity.value; 311 | if (pJS.particles.opacity.anim.enable) { 312 | this.opacity_status = false; 313 | this.vo = pJS.particles.opacity.anim.speed / 100; 314 | if (!pJS.particles.opacity.anim.sync) { 315 | this.vo = this.vo * Math.random(); 316 | } 317 | } 318 | 319 | /* animation - velocity for speed */ 320 | var velbase = {}; 321 | switch (pJS.particles.move.direction) { 322 | case "top": 323 | velbase = { x: 0, y: -1 }; 324 | break; 325 | case "top-right": 326 | velbase = { x: 0.5, y: -0.5 }; 327 | break; 328 | case "right": 329 | velbase = { x: 1, y: -0 }; 330 | break; 331 | case "bottom-right": 332 | velbase = { x: 0.5, y: 0.5 }; 333 | break; 334 | case "bottom": 335 | velbase = { x: 0, y: 1 }; 336 | break; 337 | case "bottom-left": 338 | velbase = { x: -0.5, y: 1 }; 339 | break; 340 | case "left": 341 | velbase = { x: -1, y: 0 }; 342 | break; 343 | case "top-left": 344 | velbase = { x: -0.5, y: -0.5 }; 345 | break; 346 | default: 347 | velbase = { x: 0, y: 0 }; 348 | break; 349 | } 350 | 351 | if (pJS.particles.move.straight) { 352 | this.vx = velbase.x; 353 | this.vy = velbase.y; 354 | if (pJS.particles.move.random) { 355 | this.vx = this.vx * Math.random(); 356 | this.vy = this.vy * Math.random(); 357 | } 358 | } else { 359 | this.vx = velbase.x + Math.random() - 0.5; 360 | this.vy = velbase.y + Math.random() - 0.5; 361 | } 362 | 363 | // var theta = 2.0 * Math.PI * Math.random(); 364 | // this.vx = Math.cos(theta); 365 | // this.vy = Math.sin(theta); 366 | 367 | this.vx_i = this.vx; 368 | this.vy_i = this.vy; 369 | 370 | /* if shape is image */ 371 | 372 | var shape_type = pJS.particles.shape.type; 373 | if (typeof shape_type == "object") { 374 | if (shape_type instanceof Array) { 375 | var shape_selected = 376 | shape_type[Math.floor(Math.random() * shape_type.length)]; 377 | this.shape = shape_selected; 378 | } 379 | } else { 380 | this.shape = shape_type; 381 | } 382 | 383 | if (this.shape == "image") { 384 | var sh = pJS.particles.shape; 385 | this.img = { 386 | src: sh.image.src, 387 | ratio: sh.image.width / sh.image.height, 388 | }; 389 | if (!this.img.ratio) this.img.ratio = 1; 390 | if (pJS.tmp.img_type == "svg" && pJS.tmp.source_svg != undefined) { 391 | pJS.fn.vendors.createSvgImg(this); 392 | if (pJS.tmp.pushing) { 393 | this.img.loaded = false; 394 | } 395 | } 396 | } 397 | }; 398 | 399 | pJS.fn.particle.prototype.draw = function () { 400 | var p = this; 401 | 402 | if (p.radius_bubble != undefined) { 403 | var radius = p.radius_bubble; 404 | } else { 405 | var radius = p.radius; 406 | } 407 | 408 | if (p.opacity_bubble != undefined) { 409 | var opacity = p.opacity_bubble; 410 | } else { 411 | var opacity = p.opacity; 412 | } 413 | 414 | if (p.color.rgb) { 415 | var color_value = 416 | "rgba(" + 417 | p.color.rgb.r + 418 | "," + 419 | p.color.rgb.g + 420 | "," + 421 | p.color.rgb.b + 422 | "," + 423 | opacity + 424 | ")"; 425 | } else { 426 | var color_value = 427 | "hsla(" + 428 | p.color.hsl.h + 429 | "," + 430 | p.color.hsl.s + 431 | "%," + 432 | p.color.hsl.l + 433 | "%," + 434 | opacity + 435 | ")"; 436 | } 437 | 438 | pJS.canvas.ctx.fillStyle = color_value; 439 | pJS.canvas.ctx.beginPath(); 440 | 441 | switch (p.shape) { 442 | case "circle": 443 | pJS.canvas.ctx.arc(p.x, p.y, radius, 0, Math.PI * 2, false); 444 | break; 445 | 446 | case "edge": 447 | pJS.canvas.ctx.rect(p.x - radius, p.y - radius, radius * 2, radius * 2); 448 | break; 449 | 450 | case "triangle": 451 | pJS.fn.vendors.drawShape( 452 | pJS.canvas.ctx, 453 | p.x - radius, 454 | p.y + radius / 1.66, 455 | radius * 2, 456 | 3, 457 | 2 458 | ); 459 | break; 460 | 461 | case "polygon": 462 | pJS.fn.vendors.drawShape( 463 | pJS.canvas.ctx, 464 | p.x - radius / (pJS.particles.shape.polygon.nb_sides / 3.5), // startX 465 | p.y - radius / (2.66 / 3.5), // startY 466 | (radius * 2.66) / (pJS.particles.shape.polygon.nb_sides / 3), // sideLength 467 | pJS.particles.shape.polygon.nb_sides, // sideCountNumerator 468 | 1 // sideCountDenominator 469 | ); 470 | break; 471 | 472 | case "star": 473 | pJS.fn.vendors.drawShape( 474 | pJS.canvas.ctx, 475 | p.x - (radius * 2) / (pJS.particles.shape.polygon.nb_sides / 4), // startX 476 | p.y - radius / ((2 * 2.66) / 3.5), // startY 477 | (radius * 2 * 2.66) / (pJS.particles.shape.polygon.nb_sides / 3), // sideLength 478 | pJS.particles.shape.polygon.nb_sides, // sideCountNumerator 479 | 2 // sideCountDenominator 480 | ); 481 | break; 482 | 483 | case "image": 484 | function draw() { 485 | pJS.canvas.ctx.drawImage( 486 | img_obj, 487 | p.x - radius, 488 | p.y - radius, 489 | radius * 2, 490 | (radius * 2) / p.img.ratio 491 | ); 492 | } 493 | 494 | if (pJS.tmp.img_type == "svg") { 495 | var img_obj = p.img.obj; 496 | } else { 497 | var img_obj = pJS.tmp.img_obj; 498 | } 499 | 500 | if (img_obj) { 501 | draw(); 502 | } 503 | 504 | break; 505 | } 506 | 507 | pJS.canvas.ctx.closePath(); 508 | 509 | if (pJS.particles.shape.stroke.width > 0) { 510 | pJS.canvas.ctx.strokeStyle = pJS.particles.shape.stroke.color; 511 | pJS.canvas.ctx.lineWidth = pJS.particles.shape.stroke.width; 512 | pJS.canvas.ctx.stroke(); 513 | } 514 | 515 | pJS.canvas.ctx.fill(); 516 | }; 517 | 518 | pJS.fn.particlesCreate = function () { 519 | for (var i = 0; i < pJS.particles.number.value; i++) { 520 | pJS.particles.array.push( 521 | new pJS.fn.particle(pJS.particles.color, pJS.particles.opacity.value) 522 | ); 523 | } 524 | }; 525 | 526 | pJS.fn.particlesUpdate = function () { 527 | for (var i = 0; i < pJS.particles.array.length; i++) { 528 | /* the particle */ 529 | var p = pJS.particles.array[i]; 530 | 531 | // var d = ( dx = pJS.interactivity.mouse.click_pos_x - p.x ) * dx + ( dy = pJS.interactivity.mouse.click_pos_y - p.y ) * dy; 532 | // var f = -BANG_SIZE / d; 533 | // if ( d < BANG_SIZE ) { 534 | // var t = Math.atan2( dy, dx ); 535 | // p.vx = f * Math.cos(t); 536 | // p.vy = f * Math.sin(t); 537 | // } 538 | 539 | /* move the particle */ 540 | if (pJS.particles.move.enable) { 541 | var ms = pJS.particles.move.speed / 2; 542 | p.x += p.vx * ms; 543 | p.y += p.vy * ms; 544 | } 545 | 546 | /* change opacity status */ 547 | if (pJS.particles.opacity.anim.enable) { 548 | if (p.opacity_status == true) { 549 | if (p.opacity >= pJS.particles.opacity.value) 550 | p.opacity_status = false; 551 | p.opacity += p.vo; 552 | } else { 553 | if (p.opacity <= pJS.particles.opacity.anim.opacity_min) 554 | p.opacity_status = true; 555 | p.opacity -= p.vo; 556 | } 557 | if (p.opacity < 0) p.opacity = 0; 558 | } 559 | 560 | /* change size */ 561 | if (pJS.particles.size.anim.enable) { 562 | if (p.size_status == true) { 563 | if (p.radius >= pJS.particles.size.value) p.size_status = false; 564 | p.radius += p.vs; 565 | } else { 566 | if (p.radius <= pJS.particles.size.anim.size_min) 567 | p.size_status = true; 568 | p.radius -= p.vs; 569 | } 570 | if (p.radius < 0) p.radius = 0; 571 | } 572 | 573 | /* change particle position if it is out of canvas */ 574 | if (pJS.particles.move.out_mode == "bounce") { 575 | var new_pos = { 576 | x_left: p.radius, 577 | x_right: pJS.canvas.w, 578 | y_top: p.radius, 579 | y_bottom: pJS.canvas.h, 580 | }; 581 | } else { 582 | var new_pos = { 583 | x_left: -p.radius, 584 | x_right: pJS.canvas.w + p.radius, 585 | y_top: -p.radius, 586 | y_bottom: pJS.canvas.h + p.radius, 587 | }; 588 | } 589 | 590 | if (p.x - p.radius > pJS.canvas.w) { 591 | p.x = new_pos.x_left; 592 | p.y = Math.random() * pJS.canvas.h; 593 | } else if (p.x + p.radius < 0) { 594 | p.x = new_pos.x_right; 595 | p.y = Math.random() * pJS.canvas.h; 596 | } 597 | if (p.y - p.radius > pJS.canvas.h) { 598 | p.y = new_pos.y_top; 599 | p.x = Math.random() * pJS.canvas.w; 600 | } else if (p.y + p.radius < 0) { 601 | p.y = new_pos.y_bottom; 602 | p.x = Math.random() * pJS.canvas.w; 603 | } 604 | 605 | /* out of canvas modes */ 606 | switch (pJS.particles.move.out_mode) { 607 | case "bounce": 608 | if (p.x + p.radius > pJS.canvas.w) p.vx = -p.vx; 609 | else if (p.x - p.radius < 0) p.vx = -p.vx; 610 | if (p.y + p.radius > pJS.canvas.h) p.vy = -p.vy; 611 | else if (p.y - p.radius < 0) p.vy = -p.vy; 612 | break; 613 | } 614 | 615 | /* events */ 616 | if (isInArray("grab", pJS.interactivity.events.onhover.mode)) { 617 | pJS.fn.modes.grabParticle(p); 618 | } 619 | 620 | if ( 621 | isInArray("bubble", pJS.interactivity.events.onhover.mode) || 622 | isInArray("bubble", pJS.interactivity.events.onclick.mode) 623 | ) { 624 | pJS.fn.modes.bubbleParticle(p); 625 | } 626 | 627 | if ( 628 | isInArray("repulse", pJS.interactivity.events.onhover.mode) || 629 | isInArray("repulse", pJS.interactivity.events.onclick.mode) 630 | ) { 631 | pJS.fn.modes.repulseParticle(p); 632 | } 633 | 634 | /* interaction auto between particles */ 635 | if ( 636 | pJS.particles.line_linked.enable || 637 | pJS.particles.move.attract.enable 638 | ) { 639 | for (var j = i + 1; j < pJS.particles.array.length; j++) { 640 | var p2 = pJS.particles.array[j]; 641 | 642 | /* link particles */ 643 | if (pJS.particles.line_linked.enable) { 644 | pJS.fn.interact.linkParticles(p, p2); 645 | } 646 | 647 | /* attract particles */ 648 | if (pJS.particles.move.attract.enable) { 649 | pJS.fn.interact.attractParticles(p, p2); 650 | } 651 | 652 | /* bounce particles */ 653 | if (pJS.particles.move.bounce) { 654 | pJS.fn.interact.bounceParticles(p, p2); 655 | } 656 | } 657 | } 658 | } 659 | }; 660 | 661 | pJS.fn.particlesDraw = function () { 662 | /* clear canvas */ 663 | pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h); 664 | 665 | /* update each particles param */ 666 | pJS.fn.particlesUpdate(); 667 | 668 | /* draw each particle */ 669 | for (var i = 0; i < pJS.particles.array.length; i++) { 670 | var p = pJS.particles.array[i]; 671 | p.draw(); 672 | } 673 | }; 674 | 675 | pJS.fn.particlesEmpty = function () { 676 | pJS.particles.array = []; 677 | }; 678 | 679 | pJS.fn.particlesRefresh = function () { 680 | /* init all */ 681 | cancelRequestAnimFrame(pJS.fn.checkAnimFrame); 682 | cancelRequestAnimFrame(pJS.fn.drawAnimFrame); 683 | pJS.tmp.source_svg = undefined; 684 | pJS.tmp.img_obj = undefined; 685 | pJS.tmp.count_svg = 0; 686 | pJS.fn.particlesEmpty(); 687 | pJS.fn.canvasClear(); 688 | 689 | /* restart */ 690 | pJS.fn.vendors.start(); 691 | }; 692 | 693 | /* ---------- pJS functions - particles interaction ------------ */ 694 | 695 | pJS.fn.interact.linkParticles = function (p1, p2) { 696 | var dx = p1.x - p2.x, 697 | dy = p1.y - p2.y, 698 | dist = Math.sqrt(dx * dx + dy * dy); 699 | 700 | /* draw a line between p1 and p2 if the distance between them is under the config distance */ 701 | if (dist <= pJS.particles.line_linked.distance) { 702 | var opacity_line = 703 | pJS.particles.line_linked.opacity - 704 | dist / 705 | (1 / pJS.particles.line_linked.opacity) / 706 | pJS.particles.line_linked.distance; 707 | 708 | if (opacity_line > 0) { 709 | /* style */ 710 | var color_line = pJS.particles.line_linked.color_rgb_line; 711 | pJS.canvas.ctx.strokeStyle = 712 | "rgba(" + 713 | color_line.r + 714 | "," + 715 | color_line.g + 716 | "," + 717 | color_line.b + 718 | "," + 719 | opacity_line + 720 | ")"; 721 | pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width; 722 | //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */ 723 | 724 | /* path */ 725 | pJS.canvas.ctx.beginPath(); 726 | pJS.canvas.ctx.moveTo(p1.x, p1.y); 727 | pJS.canvas.ctx.lineTo(p2.x, p2.y); 728 | pJS.canvas.ctx.stroke(); 729 | pJS.canvas.ctx.closePath(); 730 | } 731 | } 732 | }; 733 | 734 | pJS.fn.interact.attractParticles = function (p1, p2) { 735 | /* condensed particles */ 736 | var dx = p1.x - p2.x, 737 | dy = p1.y - p2.y, 738 | dist = Math.sqrt(dx * dx + dy * dy); 739 | 740 | if (dist <= pJS.particles.line_linked.distance) { 741 | var ax = dx / (pJS.particles.move.attract.rotateX * 1000), 742 | ay = dy / (pJS.particles.move.attract.rotateY * 1000); 743 | 744 | p1.vx -= ax; 745 | p1.vy -= ay; 746 | 747 | p2.vx += ax; 748 | p2.vy += ay; 749 | } 750 | }; 751 | 752 | pJS.fn.interact.bounceParticles = function (p1, p2) { 753 | var dx = p1.x - p2.x, 754 | dy = p1.y - p2.y, 755 | dist = Math.sqrt(dx * dx + dy * dy), 756 | dist_p = p1.radius + p2.radius; 757 | 758 | if (dist <= dist_p) { 759 | p1.vx = -p1.vx; 760 | p1.vy = -p1.vy; 761 | 762 | p2.vx = -p2.vx; 763 | p2.vy = -p2.vy; 764 | } 765 | }; 766 | 767 | /* ---------- pJS functions - modes events ------------ */ 768 | 769 | pJS.fn.modes.pushParticles = function (nb, pos) { 770 | pJS.tmp.pushing = true; 771 | 772 | for (var i = 0; i < nb; i++) { 773 | pJS.particles.array.push( 774 | new pJS.fn.particle(pJS.particles.color, pJS.particles.opacity.value, { 775 | x: pos ? pos.pos_x : Math.random() * pJS.canvas.w, 776 | y: pos ? pos.pos_y : Math.random() * pJS.canvas.h, 777 | }) 778 | ); 779 | if (i == nb - 1) { 780 | if (!pJS.particles.move.enable) { 781 | pJS.fn.particlesDraw(); 782 | } 783 | pJS.tmp.pushing = false; 784 | } 785 | } 786 | }; 787 | 788 | pJS.fn.modes.removeParticles = function (nb) { 789 | pJS.particles.array.splice(0, nb); 790 | if (!pJS.particles.move.enable) { 791 | pJS.fn.particlesDraw(); 792 | } 793 | }; 794 | 795 | pJS.fn.modes.bubbleParticle = function (p) { 796 | /* on hover event */ 797 | if ( 798 | pJS.interactivity.events.onhover.enable && 799 | isInArray("bubble", pJS.interactivity.events.onhover.mode) 800 | ) { 801 | var dx_mouse = p.x - pJS.interactivity.mouse.pos_x, 802 | dy_mouse = p.y - pJS.interactivity.mouse.pos_y, 803 | dist_mouse = Math.sqrt(dx_mouse * dx_mouse + dy_mouse * dy_mouse), 804 | ratio = 1 - dist_mouse / pJS.interactivity.modes.bubble.distance; 805 | 806 | function init() { 807 | p.opacity_bubble = p.opacity; 808 | p.radius_bubble = p.radius; 809 | } 810 | 811 | /* mousemove - check ratio */ 812 | if (dist_mouse <= pJS.interactivity.modes.bubble.distance) { 813 | if (ratio >= 0 && pJS.interactivity.status == "mousemove") { 814 | /* size */ 815 | if (pJS.interactivity.modes.bubble.size != pJS.particles.size.value) { 816 | if ( 817 | pJS.interactivity.modes.bubble.size > pJS.particles.size.value 818 | ) { 819 | var size = p.radius + pJS.interactivity.modes.bubble.size * ratio; 820 | if (size >= 0) { 821 | p.radius_bubble = size; 822 | } 823 | } else { 824 | var dif = p.radius - pJS.interactivity.modes.bubble.size, 825 | size = p.radius - dif * ratio; 826 | if (size > 0) { 827 | p.radius_bubble = size; 828 | } else { 829 | p.radius_bubble = 0; 830 | } 831 | } 832 | } 833 | 834 | /* opacity */ 835 | if ( 836 | pJS.interactivity.modes.bubble.opacity != 837 | pJS.particles.opacity.value 838 | ) { 839 | if ( 840 | pJS.interactivity.modes.bubble.opacity > 841 | pJS.particles.opacity.value 842 | ) { 843 | var opacity = pJS.interactivity.modes.bubble.opacity * ratio; 844 | if ( 845 | opacity > p.opacity && 846 | opacity <= pJS.interactivity.modes.bubble.opacity 847 | ) { 848 | p.opacity_bubble = opacity; 849 | } 850 | } else { 851 | var opacity = 852 | p.opacity - 853 | (pJS.particles.opacity.value - 854 | pJS.interactivity.modes.bubble.opacity) * 855 | ratio; 856 | if ( 857 | opacity < p.opacity && 858 | opacity >= pJS.interactivity.modes.bubble.opacity 859 | ) { 860 | p.opacity_bubble = opacity; 861 | } 862 | } 863 | } 864 | } 865 | } else { 866 | init(); 867 | } 868 | 869 | /* mouseleave */ 870 | if (pJS.interactivity.status == "mouseleave") { 871 | init(); 872 | } 873 | } else if ( 874 | 875 | /* on click event */ 876 | pJS.interactivity.events.onclick.enable && 877 | isInArray("bubble", pJS.interactivity.events.onclick.mode) 878 | ) { 879 | if (pJS.tmp.bubble_clicking) { 880 | var dx_mouse = p.x - pJS.interactivity.mouse.click_pos_x, 881 | dy_mouse = p.y - pJS.interactivity.mouse.click_pos_y, 882 | dist_mouse = Math.sqrt(dx_mouse * dx_mouse + dy_mouse * dy_mouse), 883 | time_spent = 884 | (new Date().getTime() - pJS.interactivity.mouse.click_time) / 1000; 885 | 886 | if (time_spent > pJS.interactivity.modes.bubble.duration) { 887 | pJS.tmp.bubble_duration_end = true; 888 | } 889 | 890 | if (time_spent > pJS.interactivity.modes.bubble.duration * 2) { 891 | pJS.tmp.bubble_clicking = false; 892 | pJS.tmp.bubble_duration_end = false; 893 | } 894 | } 895 | 896 | function process(bubble_param, particles_param, p_obj_bubble, p_obj, id) { 897 | if (bubble_param != particles_param) { 898 | if (!pJS.tmp.bubble_duration_end) { 899 | if (dist_mouse <= pJS.interactivity.modes.bubble.distance) { 900 | if (p_obj_bubble != undefined) var obj = p_obj_bubble; 901 | else var obj = p_obj; 902 | if (obj != bubble_param) { 903 | var value = 904 | p_obj - 905 | (time_spent * (p_obj - bubble_param)) / 906 | pJS.interactivity.modes.bubble.duration; 907 | if (id == "size") p.radius_bubble = value; 908 | if (id == "opacity") p.opacity_bubble = value; 909 | } 910 | } else { 911 | if (id == "size") p.radius_bubble = undefined; 912 | if (id == "opacity") p.opacity_bubble = undefined; 913 | } 914 | } else { 915 | if (p_obj_bubble != undefined) { 916 | var value_tmp = 917 | p_obj - 918 | (time_spent * (p_obj - bubble_param)) / 919 | pJS.interactivity.modes.bubble.duration, 920 | dif = bubble_param - value_tmp; 921 | value = bubble_param + dif; 922 | if (id == "size") p.radius_bubble = value; 923 | if (id == "opacity") p.opacity_bubble = value; 924 | } 925 | } 926 | } 927 | } 928 | 929 | if (pJS.tmp.bubble_clicking) { 930 | /* size */ 931 | process( 932 | pJS.interactivity.modes.bubble.size, 933 | pJS.particles.size.value, 934 | p.radius_bubble, 935 | p.radius, 936 | "size" 937 | ); 938 | /* opacity */ 939 | process( 940 | pJS.interactivity.modes.bubble.opacity, 941 | pJS.particles.opacity.value, 942 | p.opacity_bubble, 943 | p.opacity, 944 | "opacity" 945 | ); 946 | } 947 | } 948 | }; 949 | 950 | pJS.fn.modes.repulseParticle = function (p) { 951 | if ( 952 | pJS.interactivity.events.onhover.enable && 953 | isInArray("repulse", pJS.interactivity.events.onhover.mode) && 954 | pJS.interactivity.status == "mousemove" 955 | ) { 956 | var dx_mouse = p.x - pJS.interactivity.mouse.pos_x, 957 | dy_mouse = p.y - pJS.interactivity.mouse.pos_y, 958 | dist_mouse = Math.sqrt(dx_mouse * dx_mouse + dy_mouse * dy_mouse); 959 | 960 | var normVec = { x: dx_mouse / dist_mouse, y: dy_mouse / dist_mouse }, 961 | repulseRadius = pJS.interactivity.modes.repulse.distance, 962 | velocity = 100, 963 | repulseFactor = clamp( 964 | (1 / repulseRadius) * 965 | (-1 * Math.pow(dist_mouse / repulseRadius, 2) + 1) * 966 | repulseRadius * 967 | velocity, 968 | 0, 969 | 50 970 | ); 971 | 972 | var pos = { 973 | x: p.x + normVec.x * repulseFactor, 974 | y: p.y + normVec.y * repulseFactor, 975 | }; 976 | 977 | if (pJS.particles.move.out_mode == "bounce") { 978 | if (pos.x - p.radius > 0 && pos.x + p.radius < pJS.canvas.w) 979 | p.x = pos.x; 980 | if (pos.y - p.radius > 0 && pos.y + p.radius < pJS.canvas.h) 981 | p.y = pos.y; 982 | } else { 983 | p.x = pos.x; 984 | p.y = pos.y; 985 | } 986 | } else if ( 987 | pJS.interactivity.events.onclick.enable && 988 | isInArray("repulse", pJS.interactivity.events.onclick.mode) 989 | ) { 990 | if (!pJS.tmp.repulse_finish) { 991 | pJS.tmp.repulse_count++; 992 | if (pJS.tmp.repulse_count == pJS.particles.array.length) { 993 | pJS.tmp.repulse_finish = true; 994 | } 995 | } 996 | 997 | if (pJS.tmp.repulse_clicking) { 998 | var repulseRadius = Math.pow( 999 | pJS.interactivity.modes.repulse.distance / 6, 1000 | 3 1001 | ); 1002 | 1003 | var dx = pJS.interactivity.mouse.click_pos_x - p.x, 1004 | dy = pJS.interactivity.mouse.click_pos_y - p.y, 1005 | d = dx * dx + dy * dy; 1006 | 1007 | var force = (-repulseRadius / d) * 1; 1008 | 1009 | function process() { 1010 | var f = Math.atan2(dy, dx); 1011 | p.vx = force * Math.cos(f); 1012 | p.vy = force * Math.sin(f); 1013 | 1014 | if (pJS.particles.move.out_mode == "bounce") { 1015 | var pos = { 1016 | x: p.x + p.vx, 1017 | y: p.y + p.vy, 1018 | }; 1019 | if (pos.x + p.radius > pJS.canvas.w) p.vx = -p.vx; 1020 | else if (pos.x - p.radius < 0) p.vx = -p.vx; 1021 | if (pos.y + p.radius > pJS.canvas.h) p.vy = -p.vy; 1022 | else if (pos.y - p.radius < 0) p.vy = -p.vy; 1023 | } 1024 | } 1025 | 1026 | // default 1027 | if (d <= repulseRadius) { 1028 | process(); 1029 | } 1030 | 1031 | // bang - slow motion mode 1032 | // if(!pJS.tmp.repulse_finish){ 1033 | // if(d <= repulseRadius){ 1034 | // process(); 1035 | // } 1036 | // }else{ 1037 | // process(); 1038 | // } 1039 | } else { 1040 | if (pJS.tmp.repulse_clicking == false) { 1041 | p.vx = p.vx_i; 1042 | p.vy = p.vy_i; 1043 | } 1044 | } 1045 | } 1046 | }; 1047 | 1048 | pJS.fn.modes.grabParticle = function (p) { 1049 | if ( 1050 | pJS.interactivity.events.onhover.enable && 1051 | pJS.interactivity.status == "mousemove" 1052 | ) { 1053 | var dx_mouse = p.x - pJS.interactivity.mouse.pos_x, 1054 | dy_mouse = p.y - pJS.interactivity.mouse.pos_y, 1055 | dist_mouse = Math.sqrt(dx_mouse * dx_mouse + dy_mouse * dy_mouse); 1056 | 1057 | /* draw a line between the cursor and the particle if the distance between them is under the config distance */ 1058 | if (dist_mouse <= pJS.interactivity.modes.grab.distance) { 1059 | var opacity_line = 1060 | pJS.interactivity.modes.grab.line_linked.opacity - 1061 | dist_mouse / 1062 | (1 / pJS.interactivity.modes.grab.line_linked.opacity) / 1063 | pJS.interactivity.modes.grab.distance; 1064 | 1065 | if (opacity_line > 0) { 1066 | /* style */ 1067 | var color_line = pJS.particles.line_linked.color_rgb_line; 1068 | pJS.canvas.ctx.strokeStyle = 1069 | "rgba(" + 1070 | color_line.r + 1071 | "," + 1072 | color_line.g + 1073 | "," + 1074 | color_line.b + 1075 | "," + 1076 | opacity_line + 1077 | ")"; 1078 | pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width; 1079 | //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */ 1080 | 1081 | /* path */ 1082 | pJS.canvas.ctx.beginPath(); 1083 | pJS.canvas.ctx.moveTo(p.x, p.y); 1084 | pJS.canvas.ctx.lineTo( 1085 | pJS.interactivity.mouse.pos_x, 1086 | pJS.interactivity.mouse.pos_y 1087 | ); 1088 | pJS.canvas.ctx.stroke(); 1089 | pJS.canvas.ctx.closePath(); 1090 | } 1091 | } 1092 | } 1093 | }; 1094 | 1095 | /* ---------- pJS functions - vendors ------------ */ 1096 | 1097 | pJS.fn.vendors.eventsListeners = function () { 1098 | /* events target element */ 1099 | if (pJS.interactivity.detect_on == "window") { 1100 | pJS.interactivity.el = window; 1101 | } else { 1102 | pJS.interactivity.el = pJS.canvas.el; 1103 | } 1104 | 1105 | /* detect mouse pos - on hover / click event */ 1106 | if ( 1107 | pJS.interactivity.events.onhover.enable || 1108 | pJS.interactivity.events.onclick.enable 1109 | ) { 1110 | /* el on mousemove */ 1111 | pJS.interactivity.el.addEventListener("mousemove", function (e) { 1112 | if (pJS.interactivity.el == window) { 1113 | var pos_x = e.clientX, 1114 | pos_y = e.clientY; 1115 | } else { 1116 | var pos_x = e.offsetX || e.clientX, 1117 | pos_y = e.offsetY || e.clientY; 1118 | } 1119 | 1120 | pJS.interactivity.mouse.pos_x = pos_x; 1121 | pJS.interactivity.mouse.pos_y = pos_y; 1122 | 1123 | if (pJS.tmp.retina) { 1124 | pJS.interactivity.mouse.pos_x *= pJS.canvas.pxratio; 1125 | pJS.interactivity.mouse.pos_y *= pJS.canvas.pxratio; 1126 | } 1127 | 1128 | pJS.interactivity.status = "mousemove"; 1129 | }); 1130 | 1131 | /* el on onmouseleave */ 1132 | pJS.interactivity.el.addEventListener("mouseleave", function (e) { 1133 | pJS.interactivity.mouse.pos_x = null; 1134 | pJS.interactivity.mouse.pos_y = null; 1135 | pJS.interactivity.status = "mouseleave"; 1136 | }); 1137 | } 1138 | 1139 | /* on click event */ 1140 | if (pJS.interactivity.events.onclick.enable) { 1141 | pJS.interactivity.el.addEventListener("click", function () { 1142 | pJS.interactivity.mouse.click_pos_x = pJS.interactivity.mouse.pos_x; 1143 | pJS.interactivity.mouse.click_pos_y = pJS.interactivity.mouse.pos_y; 1144 | pJS.interactivity.mouse.click_time = new Date().getTime(); 1145 | 1146 | if (pJS.interactivity.events.onclick.enable) { 1147 | switch (pJS.interactivity.events.onclick.mode) { 1148 | case "push": 1149 | if (pJS.particles.move.enable) { 1150 | pJS.fn.modes.pushParticles( 1151 | pJS.interactivity.modes.push.particles_nb, 1152 | pJS.interactivity.mouse 1153 | ); 1154 | } else { 1155 | if (pJS.interactivity.modes.push.particles_nb == 1) { 1156 | pJS.fn.modes.pushParticles( 1157 | pJS.interactivity.modes.push.particles_nb, 1158 | pJS.interactivity.mouse 1159 | ); 1160 | } else if (pJS.interactivity.modes.push.particles_nb > 1) { 1161 | pJS.fn.modes.pushParticles( 1162 | pJS.interactivity.modes.push.particles_nb 1163 | ); 1164 | } 1165 | } 1166 | break; 1167 | 1168 | case "remove": 1169 | pJS.fn.modes.removeParticles( 1170 | pJS.interactivity.modes.remove.particles_nb 1171 | ); 1172 | break; 1173 | 1174 | case "bubble": 1175 | pJS.tmp.bubble_clicking = true; 1176 | break; 1177 | 1178 | case "repulse": 1179 | pJS.tmp.repulse_clicking = true; 1180 | pJS.tmp.repulse_count = 0; 1181 | pJS.tmp.repulse_finish = false; 1182 | setTimeout(function () { 1183 | pJS.tmp.repulse_clicking = false; 1184 | }, pJS.interactivity.modes.repulse.duration * 1000); 1185 | break; 1186 | } 1187 | } 1188 | }); 1189 | } 1190 | }; 1191 | 1192 | pJS.fn.vendors.densityAutoParticles = function () { 1193 | if (pJS.particles.number.density.enable) { 1194 | /* calc area */ 1195 | var area = (pJS.canvas.el.width * pJS.canvas.el.height) / 1000; 1196 | if (pJS.tmp.retina) { 1197 | area = area / (pJS.canvas.pxratio * 2); 1198 | } 1199 | 1200 | /* calc number of particles based on density area */ 1201 | var nb_particles = 1202 | (area * pJS.particles.number.value) / 1203 | pJS.particles.number.density.value_area; 1204 | 1205 | /* add or remove X particles */ 1206 | var missing_particles = pJS.particles.array.length - nb_particles; 1207 | if (missing_particles < 0) 1208 | pJS.fn.modes.pushParticles(Math.abs(missing_particles)); 1209 | else pJS.fn.modes.removeParticles(missing_particles); 1210 | } 1211 | }; 1212 | 1213 | pJS.fn.vendors.checkOverlap = function (p1, position) { 1214 | for (var i = 0; i < pJS.particles.array.length; i++) { 1215 | var p2 = pJS.particles.array[i]; 1216 | 1217 | var dx = p1.x - p2.x, 1218 | dy = p1.y - p2.y, 1219 | dist = Math.sqrt(dx * dx + dy * dy); 1220 | 1221 | if (dist <= p1.radius + p2.radius) { 1222 | p1.x = position ? position.x : Math.random() * pJS.canvas.w; 1223 | p1.y = position ? position.y : Math.random() * pJS.canvas.h; 1224 | pJS.fn.vendors.checkOverlap(p1); 1225 | } 1226 | } 1227 | }; 1228 | 1229 | pJS.fn.vendors.createSvgImg = function (p) { 1230 | /* set color to svg element */ 1231 | var svgXml = pJS.tmp.source_svg, 1232 | rgbHex = /#([0-9A-F]{3,6})/gi, 1233 | coloredSvgXml = svgXml.replace(rgbHex, function (m, r, g, b) { 1234 | if (p.color.rgb) { 1235 | var color_value = 1236 | "rgba(" + 1237 | p.color.rgb.r + 1238 | "," + 1239 | p.color.rgb.g + 1240 | "," + 1241 | p.color.rgb.b + 1242 | "," + 1243 | p.opacity + 1244 | ")"; 1245 | } else { 1246 | var color_value = 1247 | "hsla(" + 1248 | p.color.hsl.h + 1249 | "," + 1250 | p.color.hsl.s + 1251 | "%," + 1252 | p.color.hsl.l + 1253 | "%," + 1254 | p.opacity + 1255 | ")"; 1256 | } 1257 | return color_value; 1258 | }); 1259 | 1260 | /* prepare to create img with colored svg */ 1261 | var svg = new Blob([coloredSvgXml], { 1262 | type: "image/svg+xml;charset=utf-8", 1263 | }), 1264 | DOMURL = window.URL || window.webkitURL || window, 1265 | url = DOMURL.createObjectURL(svg); 1266 | 1267 | /* create particle img obj */ 1268 | var img = new Image(); 1269 | img.addEventListener("load", function () { 1270 | p.img.obj = img; 1271 | p.img.loaded = true; 1272 | DOMURL.revokeObjectURL(url); 1273 | pJS.tmp.count_svg++; 1274 | }); 1275 | img.src = url; 1276 | }; 1277 | 1278 | pJS.fn.vendors.destroypJS = function () { 1279 | cancelAnimationFrame(pJS.fn.drawAnimFrame); 1280 | canvas_el.remove(); 1281 | pJSDom = null; 1282 | }; 1283 | 1284 | pJS.fn.vendors.drawShape = function ( 1285 | c, 1286 | startX, 1287 | startY, 1288 | sideLength, 1289 | sideCountNumerator, 1290 | sideCountDenominator 1291 | ) { 1292 | // By Programming Thomas - https://programmingthomas.wordpress.com/2013/04/03/n-sided-shapes/ 1293 | var sideCount = sideCountNumerator * sideCountDenominator; 1294 | var decimalSides = sideCountNumerator / sideCountDenominator; 1295 | var interiorAngleDegrees = (180 * (decimalSides - 2)) / decimalSides; 1296 | var interiorAngle = Math.PI - (Math.PI * interiorAngleDegrees) / 180; // convert to radians 1297 | c.save(); 1298 | c.beginPath(); 1299 | c.translate(startX, startY); 1300 | c.moveTo(0, 0); 1301 | for (var i = 0; i < sideCount; i++) { 1302 | c.lineTo(sideLength, 0); 1303 | c.translate(sideLength, 0); 1304 | c.rotate(interiorAngle); 1305 | } 1306 | //c.stroke(); 1307 | c.fill(); 1308 | c.restore(); 1309 | }; 1310 | 1311 | pJS.fn.vendors.exportImg = function () { 1312 | window.open(pJS.canvas.el.toDataURL("image/png"), "_blank"); 1313 | }; 1314 | 1315 | pJS.fn.vendors.loadImg = function (type) { 1316 | pJS.tmp.img_error = undefined; 1317 | 1318 | if (pJS.particles.shape.image.src != "") { 1319 | if (type == "svg") { 1320 | var xhr = new XMLHttpRequest(); 1321 | xhr.open("GET", pJS.particles.shape.image.src); 1322 | xhr.onreadystatechange = function (data) { 1323 | if (xhr.readyState == 4) { 1324 | if (xhr.status == 200) { 1325 | pJS.tmp.source_svg = data.currentTarget.response; 1326 | pJS.fn.vendors.checkBeforeDraw(); 1327 | } else { 1328 | console.log("Error pJS - Image not found"); 1329 | pJS.tmp.img_error = true; 1330 | } 1331 | } 1332 | }; 1333 | xhr.send(); 1334 | } else { 1335 | var img = new Image(); 1336 | img.addEventListener("load", function () { 1337 | pJS.tmp.img_obj = img; 1338 | pJS.fn.vendors.checkBeforeDraw(); 1339 | }); 1340 | img.src = pJS.particles.shape.image.src; 1341 | } 1342 | } else { 1343 | console.log("Error pJS - No image.src"); 1344 | pJS.tmp.img_error = true; 1345 | } 1346 | }; 1347 | 1348 | pJS.fn.vendors.draw = function () { 1349 | if (pJS.particles.shape.type == "image") { 1350 | if (pJS.tmp.img_type == "svg") { 1351 | if (pJS.tmp.count_svg >= pJS.particles.number.value) { 1352 | pJS.fn.particlesDraw(); 1353 | if (!pJS.particles.move.enable) 1354 | cancelRequestAnimFrame(pJS.fn.drawAnimFrame); 1355 | else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); 1356 | } else { 1357 | //console.log('still loading...'); 1358 | if (!pJS.tmp.img_error) 1359 | pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); 1360 | } 1361 | } else { 1362 | if (pJS.tmp.img_obj != undefined) { 1363 | pJS.fn.particlesDraw(); 1364 | if (!pJS.particles.move.enable) 1365 | cancelRequestAnimFrame(pJS.fn.drawAnimFrame); 1366 | else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); 1367 | } else { 1368 | if (!pJS.tmp.img_error) 1369 | pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); 1370 | } 1371 | } 1372 | } else { 1373 | pJS.fn.particlesDraw(); 1374 | if (!pJS.particles.move.enable) 1375 | cancelRequestAnimFrame(pJS.fn.drawAnimFrame); 1376 | else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); 1377 | } 1378 | }; 1379 | 1380 | pJS.fn.vendors.checkBeforeDraw = function () { 1381 | // if shape is image 1382 | if (pJS.particles.shape.type == "image") { 1383 | if (pJS.tmp.img_type == "svg" && pJS.tmp.source_svg == undefined) { 1384 | pJS.tmp.checkAnimFrame = requestAnimFrame(check); 1385 | } else { 1386 | //console.log('images loaded! cancel check'); 1387 | cancelRequestAnimFrame(pJS.tmp.checkAnimFrame); 1388 | if (!pJS.tmp.img_error) { 1389 | pJS.fn.vendors.init(); 1390 | pJS.fn.vendors.draw(); 1391 | } 1392 | } 1393 | } else { 1394 | pJS.fn.vendors.init(); 1395 | pJS.fn.vendors.draw(); 1396 | } 1397 | }; 1398 | 1399 | pJS.fn.vendors.init = function () { 1400 | /* init canvas + particles */ 1401 | pJS.fn.retinaInit(); 1402 | pJS.fn.canvasInit(); 1403 | pJS.fn.canvasSize(); 1404 | pJS.fn.canvasPaint(); 1405 | pJS.fn.particlesCreate(); 1406 | pJS.fn.vendors.densityAutoParticles(); 1407 | 1408 | /* particles.line_linked - convert hex colors to rgb */ 1409 | pJS.particles.line_linked.color_rgb_line = hexToRgb( 1410 | pJS.particles.line_linked.color 1411 | ); 1412 | }; 1413 | 1414 | pJS.fn.vendors.start = function () { 1415 | if (isInArray("image", pJS.particles.shape.type)) { 1416 | pJS.tmp.img_type = pJS.particles.shape.image.src.substr( 1417 | pJS.particles.shape.image.src.length - 3 1418 | ); 1419 | pJS.fn.vendors.loadImg(pJS.tmp.img_type); 1420 | } else { 1421 | pJS.fn.vendors.checkBeforeDraw(); 1422 | } 1423 | }; 1424 | 1425 | /* ---------- pJS - start ------------ */ 1426 | 1427 | pJS.fn.vendors.eventsListeners(); 1428 | 1429 | pJS.fn.vendors.start(); 1430 | }; 1431 | 1432 | /* ---------- global functions - vendors ------------ */ 1433 | 1434 | Object.deepExtend = function (destination, source) { 1435 | for (var property in source) { 1436 | if ( 1437 | source[property] && 1438 | source[property].constructor && 1439 | source[property].constructor === Object 1440 | ) { 1441 | destination[property] = destination[property] || {}; 1442 | arguments.callee(destination[property], source[property]); 1443 | } else { 1444 | destination[property] = source[property]; 1445 | } 1446 | } 1447 | return destination; 1448 | }; 1449 | 1450 | window.requestAnimFrame = (function () { 1451 | return ( 1452 | window.requestAnimationFrame || 1453 | window.webkitRequestAnimationFrame || 1454 | window.mozRequestAnimationFrame || 1455 | window.oRequestAnimationFrame || 1456 | window.msRequestAnimationFrame || 1457 | function (callback) { 1458 | window.setTimeout(callback, 1000 / 60); 1459 | } 1460 | ); 1461 | })(); 1462 | 1463 | window.cancelRequestAnimFrame = (function () { 1464 | return ( 1465 | window.cancelAnimationFrame || 1466 | window.webkitCancelRequestAnimationFrame || 1467 | window.mozCancelRequestAnimationFrame || 1468 | window.oCancelRequestAnimationFrame || 1469 | window.msCancelRequestAnimationFrame || 1470 | clearTimeout 1471 | ); 1472 | })(); 1473 | 1474 | function hexToRgb(hex) { 1475 | // By Tim Down - http://stackoverflow.com/a/5624139/3493650 1476 | // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") 1477 | var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; 1478 | hex = hex.replace(shorthandRegex, function (m, r, g, b) { 1479 | return r + r + g + g + b + b; 1480 | }); 1481 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 1482 | return result 1483 | ? { 1484 | r: parseInt(result[1], 16), 1485 | g: parseInt(result[2], 16), 1486 | b: parseInt(result[3], 16), 1487 | } 1488 | : null; 1489 | } 1490 | 1491 | function clamp(number, min, max) { 1492 | return Math.min(Math.max(number, min), max); 1493 | } 1494 | 1495 | function isInArray(value, array) { 1496 | return array.indexOf(value) > -1; 1497 | } 1498 | 1499 | /* ---------- particles.js functions - start ------------ */ 1500 | 1501 | window.pJSDom = []; 1502 | 1503 | window.particlesJS = function (tag_id, params) { 1504 | //console.log(params); 1505 | 1506 | /* no string id? so it's object params, and set the id with default id */ 1507 | if (typeof tag_id != "string") { 1508 | params = tag_id; 1509 | tag_id = "particles-js"; 1510 | } 1511 | 1512 | /* no id? set the id to default id */ 1513 | if (!tag_id) { 1514 | tag_id = "particles-js"; 1515 | } 1516 | 1517 | /* pJS elements */ 1518 | var pJS_tag = document.getElementById(tag_id), 1519 | pJS_canvas_class = "particles-js-canvas-el", 1520 | exist_canvas = pJS_tag.getElementsByClassName(pJS_canvas_class); 1521 | 1522 | /* remove canvas if exists into the pJS target tag */ 1523 | if (exist_canvas.length) { 1524 | while (exist_canvas.length > 0) { 1525 | pJS_tag.removeChild(exist_canvas[0]); 1526 | } 1527 | } 1528 | 1529 | /* create canvas element */ 1530 | var canvas_el = document.createElement("canvas"); 1531 | canvas_el.className = pJS_canvas_class; 1532 | 1533 | /* set size canvas */ 1534 | canvas_el.style.width = "100%"; 1535 | canvas_el.style.height = "100%"; 1536 | 1537 | /* append canvas */ 1538 | var canvas = document.getElementById(tag_id).appendChild(canvas_el); 1539 | 1540 | /* launch particle.js */ 1541 | if (canvas != null) { 1542 | pJSDom.push(new pJS(tag_id, params)); 1543 | } 1544 | }; 1545 | 1546 | window.particlesJS.load = function (tag_id, path_config_json, callback) { 1547 | /* load json config */ 1548 | var xhr = new XMLHttpRequest(); 1549 | xhr.open("GET", path_config_json); 1550 | xhr.onreadystatechange = function (data) { 1551 | if (xhr.readyState == 4) { 1552 | if (xhr.status == 200) { 1553 | var params = JSON.parse(data.currentTarget.response); 1554 | window.particlesJS(tag_id, params); 1555 | if (callback) callback(); 1556 | } else { 1557 | console.log("Error pJS - XMLHttpRequest status: " + xhr.status); 1558 | console.log("Error pJS - File config not found"); 1559 | } 1560 | } 1561 | }; 1562 | xhr.send(); 1563 | }; 1564 | -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | Bifrost Inc. 10 | 11 | 12 | 13 |
14 | 16 | 17 | 18 | 20 | 21 | 26 | 34 | 42 | 53 | 63 | 74 | 88 | 92 | 97 | 102 | 107 | 112 | 117 | 122 | 127 | 152 | 153 | 155 | 160 | 162 | 169 | 176 | 179 | 187 | 188 | 195 | 202 | 205 | 213 | 214 | 266 | 318 | 324 | 330 | 333 | 374 | 375 | 378 | 419 | 420 | 445 | 470 | 495 | 520 | 575 | 600 | 606 | 612 | 613 | 614 | 615 | 616 | 624 | 627 | 650 | 652 | 654 | 676 | 680 | 685 | 686 | 693 | 695 | 698 | 701 | 702 | 709 | 711 | 714 | 717 | 718 | 725 | 727 | 730 | 733 | 734 | 741 | 743 | 746 | 749 | 750 | 751 | 752 |
753 |
755 |

404 756 | Error

757 |

Couldn't 758 | launch 759 | :(

760 |

Page 761 | Not 762 | Found 763 | - 764 | lets 765 | take 766 | you 767 | BACK

769 |
770 | 771 | --------------------------------------------------------------------------------