Zipcall acquired.
remove .gitattributes zipcall acquired
@ -1,6 +0,0 @@
|
|||||||
# Required for all uses
|
|
||||||
# It takes 2 mins to get a free twilio account https://www.twilio.com/login
|
|
||||||
# Fill in your credentials below and rename this file to .env
|
|
||||||
|
|
||||||
TWILIO_ACCOUNT_SID=YourAccountSIDHere
|
|
||||||
LOCAL_AUTH_TOKEN=YourAuthTokenHere
|
|
2
.gitattributes
vendored
@ -1,2 +0,0 @@
|
|||||||
* linguist-vendored
|
|
||||||
*.js linguist-vendored=false
|
|
4
.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
.DS_Store
|
|
||||||
/.idea
|
|
||||||
.env
|
|
87
LICENSE
@ -1,87 +0,0 @@
|
|||||||
Creative Commons Attribution-NonCommercial 4.0 International Public License
|
|
||||||
By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
|
|
||||||
|
|
||||||
Section 1 – Definitions.
|
|
||||||
|
|
||||||
Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
|
|
||||||
Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
|
|
||||||
Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
|
|
||||||
Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
|
|
||||||
Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
|
|
||||||
Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
|
|
||||||
Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
|
|
||||||
Licensor means the individual(s) or entity(ies) granting rights under this Public License.
|
|
||||||
NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
|
|
||||||
Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
|
|
||||||
Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
|
|
||||||
You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
|
|
||||||
Section 2 – Scope.
|
|
||||||
|
|
||||||
License grant.
|
|
||||||
Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
|
|
||||||
reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
|
|
||||||
produce, reproduce, and Share Adapted Material for NonCommercial purposes only.
|
|
||||||
Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
|
|
||||||
Term. The term of this Public License is specified in Section 6(a).
|
|
||||||
Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
|
|
||||||
Downstream recipients.
|
|
||||||
Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
|
|
||||||
No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
|
|
||||||
No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
|
|
||||||
Other rights.
|
|
||||||
|
|
||||||
Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
|
|
||||||
Patent and trademark rights are not licensed under this Public License.
|
|
||||||
To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
|
|
||||||
Section 3 – License Conditions.
|
|
||||||
|
|
||||||
Your exercise of the Licensed Rights is expressly made subject to the following conditions.
|
|
||||||
|
|
||||||
Attribution.
|
|
||||||
|
|
||||||
If You Share the Licensed Material (including in modified form), You must:
|
|
||||||
|
|
||||||
retain the following if it is supplied by the Licensor with the Licensed Material:
|
|
||||||
identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
|
|
||||||
a copyright notice;
|
|
||||||
a notice that refers to this Public License;
|
|
||||||
a notice that refers to the disclaimer of warranties;
|
|
||||||
a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
|
|
||||||
indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
|
|
||||||
indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
|
|
||||||
You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
|
|
||||||
If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
|
|
||||||
If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
|
|
||||||
Section 4 – Sui Generis Database Rights.
|
|
||||||
|
|
||||||
Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
|
|
||||||
|
|
||||||
for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;
|
|
||||||
if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
|
|
||||||
You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
|
|
||||||
For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
|
|
||||||
Section 5 – Disclaimer of Warranties and Limitation of Liability.
|
|
||||||
|
|
||||||
Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
|
|
||||||
To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
|
|
||||||
The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
|
|
||||||
Section 6 – Term and Termination.
|
|
||||||
|
|
||||||
This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
|
|
||||||
Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
|
|
||||||
|
|
||||||
automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
|
|
||||||
upon express reinstatement by the Licensor.
|
|
||||||
For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
|
|
||||||
For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
|
|
||||||
Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
|
|
||||||
Section 7 – Other Terms and Conditions.
|
|
||||||
|
|
||||||
The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
|
|
||||||
Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
|
|
||||||
Section 8 – Interpretation.
|
|
||||||
|
|
||||||
For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
|
|
||||||
To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
|
|
||||||
No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
|
|
||||||
Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
|
|
53
README.md
@ -1,23 +1,21 @@
|
|||||||
# Zipcall - Decentralized Video Chat
|
# Zipcall - Acquired at 250k users
|
||||||
|
|
||||||
[![Author](https://img.shields.io/badge/Author-ianramzy-brightgreen.svg)](https://ianramzy.com)
|
[![Author](https://img.shields.io/badge/Author-ianramzy-brightgreen.svg)](https://ianramzy.com)
|
||||||
![License: CC-NC](https://img.shields.io/badge/License-CCNC-blue.svg)
|
|
||||||
[![Donate](https://img.shields.io/badge/Donate-PayPal-brightgreen.svg)](https://paypal.me/ianramzy)
|
[![Donate](https://img.shields.io/badge/Donate-PayPal-brightgreen.svg)](https://paypal.me/ianramzy)
|
||||||
[![Repo Link](https://img.shields.io/badge/Repo-Link-black.svg)](https://github.com/ianramzy/decentralized-video-chat)
|
[![Repo Link](https://img.shields.io/badge/Repo-Link-black.svg)](https://github.com/ianramzy/decentralized-video-chat)
|
||||||
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?)](https://github.com/prettier/prettier)
|
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?)](https://github.com/prettier/prettier)
|
||||||
[![Join the chat at https://gitter.im/zipcall](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/zipcall)
|
[![Join the chat at https://gitter.im/zipcall](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/zipcall)
|
||||||
|
|
||||||
# https://zipcall.io
|
# Source code has been removed from this repository as a result of the acquisition.
|
||||||
|
|
||||||
Decentralized video chat platform powered by WebRTC using Twilio STUN/TURN infrastructure.
|
Decentralized video chat platform with video quality and latency simply not available with traditional
|
||||||
Zipcall provides video quality and latency simply not available with traditional
|
|
||||||
technology.
|
technology.
|
||||||
|
|
||||||
![screenshot](public/images/readmecall.png "Video Calling")
|
![screenshot](images/readmecall.png "Video Calling")
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
<img align="right" width="400" height="auto" src="public/images/preview.gif">
|
<img align="right" width="400" height="auto" src="images/preview.gif">
|
||||||
|
|
||||||
- Screen sharing
|
- Screen sharing
|
||||||
- Picture in picture
|
- Picture in picture
|
||||||
@ -27,44 +25,3 @@ technology.
|
|||||||
- No download required, entirely browser based
|
- No download required, entirely browser based
|
||||||
- Direct peer to peer connection ensures lowest latency
|
- Direct peer to peer connection ensures lowest latency
|
||||||
- Single use disposable chat rooms
|
- Single use disposable chat rooms
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
- You will need to have Node.js installed, this project has been tested with Node version 10.X and 12.X
|
|
||||||
- Clone this repo
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone https://github.com/ianramzy/decentralized-video-chat
|
|
||||||
cd decentralized-video-chat
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Set up credentials
|
|
||||||
|
|
||||||
- Rename .env.template to .env
|
|
||||||
- Sign up for free twilio account https://www.twilio.com/login
|
|
||||||
- Get your Account SID and Auth Token from the Twillio console
|
|
||||||
- Fill in your credentials in the .env file
|
|
||||||
|
|
||||||
#### Install dependencies
|
|
||||||
|
|
||||||
```
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Start the server
|
|
||||||
|
|
||||||
```
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
- Open `localhost:3000` in browser
|
|
||||||
- If you want to use a client on another computer/network, make sure you publish your server on an HTTPS connection.
|
|
||||||
You can use a service like [ngrok](https://ngrok.com/) for that.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Pull Requests are welcome!
|
|
||||||
|
|
||||||
Please run prettier on all of your PRs before submitting, this can be done with `prettier --write` in the project directory
|
|
||||||
|
|
||||||
For communication we use Gitter Chat which can be found here: [![Join the chat at https://gitter.im/zipcall](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/zipcall)
|
|
||||||
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 358 KiB After Width: | Height: | Size: 358 KiB |
1217
package-lock.json
generated
22
package.json
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "video-chat2",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "server.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "node server.js",
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "See License",
|
|
||||||
"dependencies": {
|
|
||||||
"dotenv": "^8.1.0",
|
|
||||||
"express": "^4.17.1",
|
|
||||||
"heroku-ssl-redirect": "0.0.4",
|
|
||||||
"socket.io": "^2.2.0",
|
|
||||||
"twilio": "^3.34.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"prettier": "2.0.2"
|
|
||||||
}
|
|
||||||
}
|
|
138
public/chat.html
@ -1,138 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
||||||
<script
|
|
||||||
async
|
|
||||||
src="https://www.googletagmanager.com/gtag/js?id=UA-162048272-1"
|
|
||||||
></script>
|
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
|
|
||||||
function gtag() {
|
|
||||||
dataLayer.push(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
gtag("js", new Date());
|
|
||||||
gtag("config", "UA-162048272-1");
|
|
||||||
</script>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Zipcall</title>
|
|
||||||
<link rel="shortcut icon" href="/images/logo.svg" />
|
|
||||||
<link rel="stylesheet" href="../css/chat.css" />
|
|
||||||
<link rel="stylesheet" href="../css/snackbar.css" />
|
|
||||||
<script
|
|
||||||
src="https://kit.fontawesome.com/9d7bb7e31a.js"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
></script>
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
|
|
||||||
<script src="/js/jquery.ui.touch-punch.js"></script>
|
|
||||||
<meta property="og:title" content="Join your friends call - Zipcall" />
|
|
||||||
<meta
|
|
||||||
property="og:description"
|
|
||||||
content="Click the link to join this call using Zipcall"
|
|
||||||
/>
|
|
||||||
<meta property="og:image" content="https://zipcall.io/images/preview.png" />
|
|
||||||
<meta property="og:url" content="https://zipcall.io/" />
|
|
||||||
</head>
|
|
||||||
<body id="body" onresize="windowResized()">
|
|
||||||
<div id="header">
|
|
||||||
<a target="_blank" href="/">
|
|
||||||
<img src="/images/logo.svg" alt="Neon" width="48" height="48" />
|
|
||||||
<p>Zipcall</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p id="remote-video-text"></p>
|
|
||||||
<video id="remote-video" autoplay playsinline></video>
|
|
||||||
<div id="moveable">
|
|
||||||
<p id="local-video-text">No webcam input</p>
|
|
||||||
<video id="local-video" autoplay muted playsinline></video>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="entire-chat">
|
|
||||||
<div id="chat-zone">
|
|
||||||
<div class="chat-messages"></div>
|
|
||||||
</div>
|
|
||||||
<form class="compose">
|
|
||||||
<input type="text" placeholder="Type a message" />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="multi-button">
|
|
||||||
<div class="buttonContainer">
|
|
||||||
<button class="hoverButton" onclick="{muteMicrophone()}">
|
|
||||||
<i id="mic-icon" class="fas fa-microphone fa-xs"></i>
|
|
||||||
</button>
|
|
||||||
<div class="HoverState" id="mic-text">Mute</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="buttonContainer">-->
|
|
||||||
<!-- <button class="hoverButton" onclick="{openFullscreen()}">-->
|
|
||||||
<!-- <i class="fas fa-compress fa-xs"></i>-->
|
|
||||||
<!-- </button>-->
|
|
||||||
<!-- <div class="HoverState">Fullscreen</div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
|
|
||||||
<div class="buttonContainer">
|
|
||||||
<button class="hoverButton" onclick="{pauseVideo()}">
|
|
||||||
<i class="fas fa-video fa-xs" id="video-icon"></i>
|
|
||||||
</button>
|
|
||||||
<div class="HoverState" id="video-text">Pause Video</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="buttonContainer">
|
|
||||||
<button class="hoverButton" id="share-button" onclick="{swap()}">
|
|
||||||
<i id="swap-icon" class="fas fa-desktop fa-xs"></i>
|
|
||||||
</button>
|
|
||||||
<div class="HoverState" id="swap-text">Share Screen</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="buttonContainer">
|
|
||||||
<button class="hoverButton" onclick="{toggleChat()}">
|
|
||||||
<i id="chat-icon" class="fas fa-comment fa-xs"></i>
|
|
||||||
</button>
|
|
||||||
<div class="HoverState" id="chat-text">Show Chat</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="buttonContainer">
|
|
||||||
<button
|
|
||||||
class="hoverButton"
|
|
||||||
id="pip-button"
|
|
||||||
onclick="{togglePictureInPicture()}"
|
|
||||||
>
|
|
||||||
<i class="fas fa-external-link-alt fa-xs"></i>
|
|
||||||
</button>
|
|
||||||
<div class="HoverState" id="pip-text">Toggle Picture in Picture</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="buttonContainer">
|
|
||||||
<button class="hoverButton" onclick="{requestToggleCaptions()}">
|
|
||||||
<i class="fas fa-closed-captioning fa-xs"></i>
|
|
||||||
</button>
|
|
||||||
<div class="HoverState" id="caption-button-text">
|
|
||||||
Start Live Caption
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="buttonContainer">
|
|
||||||
<button
|
|
||||||
class="hoverButton"
|
|
||||||
onclick="{window.location.href = '/newcall'}"
|
|
||||||
>
|
|
||||||
<i class="fas fa-phone-slash fa-xs"></i>
|
|
||||||
</button>
|
|
||||||
<div class="HoverState">End Call</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
|
|
||||||
<script src="/socket.io/socket.io.js"></script>
|
|
||||||
<script src="../js/snackbar.js"></script>
|
|
||||||
<script src="../js/autolink.js"></script>
|
|
||||||
<script src="https://cdn.rawgit.com/muaz-khan/DetectRTC/master/DetectRTC.js"></script>
|
|
||||||
<script id="chatJS" src="../js/chat.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,572 +0,0 @@
|
|||||||
@import url("https://fonts.googleapis.com/css?family=Fira+Sans:600|Heebo:400,500,700&display=swap");
|
|
||||||
|
|
||||||
/*Fade in page on load*/
|
|
||||||
@-webkit-keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-moz-keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* End Fade in page on load*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #16171b;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
opacity: 0; /* make things invisible upon start */
|
|
||||||
-webkit-animation: fadeIn ease-in 1;
|
|
||||||
-moz-animation: fadeIn ease-in 1;
|
|
||||||
animation: fadeIn ease-in 1;
|
|
||||||
-webkit-animation-fill-mode: forwards;
|
|
||||||
-moz-animation-fill-mode: forwards;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
-webkit-animation-duration: 0.3s;
|
|
||||||
-moz-animation-duration: 0.3s;
|
|
||||||
animation-duration: 0.3s;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
video {
|
|
||||||
background: #16171a;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
position: absolute;
|
|
||||||
color: white;
|
|
||||||
font-family: "Fira Sans", sans-serif;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 1rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
top: 20px;
|
|
||||||
left: 20px;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header p,
|
|
||||||
img {
|
|
||||||
float: left;
|
|
||||||
padding: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header,
|
|
||||||
a {
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Moveable local video id*/
|
|
||||||
#moveable {
|
|
||||||
z-index: 100;
|
|
||||||
position: absolute;
|
|
||||||
width: 15%;
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Text inside local video*/
|
|
||||||
#moveable p {
|
|
||||||
z-index: 101;
|
|
||||||
position: absolute;
|
|
||||||
color: white;
|
|
||||||
font-family: "Heebo", sans-serif;
|
|
||||||
white-space: nowrap;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
-ms-transform: translate(-50%, -50%);
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
font-weight: bold;
|
|
||||||
background: rgba(0, 0, 0, 0.12);
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#local-video {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 10px;
|
|
||||||
-webkit-transform: scaleX(-1);
|
|
||||||
transform: scaleX(-1);
|
|
||||||
background: #16171a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*caption text*/
|
|
||||||
#remote-video-text {
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: 0;
|
|
||||||
width: 65vw;
|
|
||||||
position: absolute;
|
|
||||||
top: calc(80%);
|
|
||||||
left: 20vw;
|
|
||||||
z-index: 1;
|
|
||||||
color: white;
|
|
||||||
font-family: "Heebo", sans-serif;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: left;
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
border-radius: 0 0 10px 10px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#remote-video {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
-ms-transform: translate(-50%, -50%);
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 65%;
|
|
||||||
height: auto;
|
|
||||||
max-height: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
border-radius: 10px;
|
|
||||||
background-image: url(../images/loader.gif);
|
|
||||||
background-size: 400px auto;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#remote-video.fullscreen {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Buttons*/
|
|
||||||
.multi-button button {
|
|
||||||
border: none;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
color: gray;
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 7px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multi-button {
|
|
||||||
position: absolute;
|
|
||||||
left: calc(7.5vw - 40px);
|
|
||||||
top: 50%;
|
|
||||||
-ms-transform: translate(0%, -50%);
|
|
||||||
transform: translate(0%, -50%);
|
|
||||||
z-index: 999;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #16171a;
|
|
||||||
box-shadow: 9px 9px 16px #0a0b0c, -9px -9px 16px #222328;
|
|
||||||
padding: 15px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-around;
|
|
||||||
/* grid-gap: 0.2rem; */
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.HoverState {
|
|
||||||
color: white;
|
|
||||||
font-family: "Heebo", sans-serif;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
position: absolute;
|
|
||||||
left: 60px;
|
|
||||||
white-space: nowrap;
|
|
||||||
top: 0px;
|
|
||||||
font-weight: bold;
|
|
||||||
background: #16171a;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainer {
|
|
||||||
position: relative;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*.fa-phone-slash {*/
|
|
||||||
/* color: #470000;*/
|
|
||||||
/*}*/
|
|
||||||
|
|
||||||
/*End buttons*/
|
|
||||||
|
|
||||||
/*Text chat*/
|
|
||||||
#entire-chat {
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
right: 0;
|
|
||||||
width: 17.5vw;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compose {
|
|
||||||
width: calc(17.5vw - 40px);
|
|
||||||
height: 60px;
|
|
||||||
font-family: "Heebo", sans-serif;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 6px 6px 12px #030506, -6px -6px 12px #292a30;
|
|
||||||
margin: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 16px;
|
|
||||||
background: #1c1d22;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow: -moz-scrollbars-none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
scrollbar-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compose input {
|
|
||||||
/*font-family: inherit;*/
|
|
||||||
font-size: 0.8rem;
|
|
||||||
border: none;
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100% - 5px);
|
|
||||||
resize: none;
|
|
||||||
outline: none;
|
|
||||||
background-color: inherit;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compose input::placeholder {
|
|
||||||
color: white;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-zone {
|
|
||||||
padding-top: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
right: 0;
|
|
||||||
width: 17.5vw;
|
|
||||||
overflow: scroll;
|
|
||||||
font-family: "Heebo", sans-serif;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-zone::-webkit-scrollbar {
|
|
||||||
width: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-zone .chat-messages .message-item {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-end;
|
|
||||||
padding: 0 16px 4px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-messages {
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding-bottom: 80px;
|
|
||||||
overflow: -moz-scrollbars-none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-zone .chat-messages .message-item.customer {
|
|
||||||
padding-left: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*received messages*/
|
|
||||||
#chat-zone .message-item.moderator .message-bloc {
|
|
||||||
background-color: rgb(29, 30, 33);
|
|
||||||
color: #fff;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 12px;
|
|
||||||
max-width: 100%;
|
|
||||||
border-radius: 20px 20px 20px 5px;
|
|
||||||
box-shadow: 6px 6px 12px #030506, -6px -6px 12px #23242a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*sent messages*/
|
|
||||||
#chat-zone .message-item.customer .message-bloc {
|
|
||||||
background-color: rgb(47, 48, 52);
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 20px 20px 5px 20px;
|
|
||||||
box-shadow: 6px 6px 12px #030506, -6px -6px 12px #22232a;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-zone .chat-messages .message-item.customer .message-bloc {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-zone .chat-messages .message-item .message-bloc {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 12px;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message a {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cssanimation {
|
|
||||||
animation-duration: 1s;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fadeInBottom {
|
|
||||||
animation-name: fadeInBottom;
|
|
||||||
}
|
|
||||||
@keyframes fadeInBottom {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(200%);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*End text chat*/
|
|
||||||
|
|
||||||
/* Begin mobile layout */
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
/* @media (min-width: 320px) and (max-width: 480px) { */
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
z-index: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header img {
|
|
||||||
width: auto;
|
|
||||||
height: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header p {
|
|
||||||
margin: 0;
|
|
||||||
line-height: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#remote-video {
|
|
||||||
/* width: 75vw;
|
|
||||||
height: calc((16/9) * 75vw); */
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
/* border: 3px solid red; */
|
|
||||||
}
|
|
||||||
|
|
||||||
#moveable {
|
|
||||||
position: fixed;
|
|
||||||
top: 5rem;
|
|
||||||
left: 5rem;
|
|
||||||
min-width: 20%;
|
|
||||||
min-height: 10%;
|
|
||||||
width: initial;
|
|
||||||
height: initial;
|
|
||||||
max-width: 25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#moveable #local-video {
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#moveable p {
|
|
||||||
text-align: center;
|
|
||||||
z-index: 101;
|
|
||||||
position: absolute;
|
|
||||||
color: white;
|
|
||||||
font-family: "Heebo", sans-serif;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
-ms-transform: translate(-50%, -50%);
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1rem;
|
|
||||||
background: rgba(0, 0, 0, 0.12);
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#remote-video-text {
|
|
||||||
position: fixed;
|
|
||||||
top: 60% !important;
|
|
||||||
bottom: initial;
|
|
||||||
left: initial;
|
|
||||||
width: 80%;
|
|
||||||
height: auto;
|
|
||||||
margin: 0;
|
|
||||||
margin-left: 10%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multi-button {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: initial;
|
|
||||||
bottom: 0;
|
|
||||||
width: 80vw;
|
|
||||||
height: 3rem;
|
|
||||||
margin: 0 10vw;
|
|
||||||
-ms-transform: translate(0%, 0%);
|
|
||||||
transform: translate(0%, 0%);
|
|
||||||
z-index: 999;
|
|
||||||
box-shadow: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 0;
|
|
||||||
/* display: flex; */
|
|
||||||
flex-direction: row;
|
|
||||||
/* overflow: hidden; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainer {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 3rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
min-width: 0;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
/* line-height: 1.5rem; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.multi-button button {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
font-size: 2rem;
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
color: gray;
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 7px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.HoverState {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonContainer:nth-child(3),
|
|
||||||
.buttonContainer:nth-child(5) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#entire-chat {
|
|
||||||
position: absolute;
|
|
||||||
top: 3rem;
|
|
||||||
right: 0;
|
|
||||||
height: calc(100% - 3rem - 3rem);
|
|
||||||
width: 100vw;
|
|
||||||
/* padding: 1rem 0; */
|
|
||||||
padding: 0;
|
|
||||||
/* border: 3px solid green; */
|
|
||||||
text-align: center;
|
|
||||||
/* background: #16171a; */
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-zone {
|
|
||||||
position: relative;
|
|
||||||
padding-top: 20px;
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100% - 2rem);
|
|
||||||
margin-left: 0;
|
|
||||||
/* box-shadow: 4px 4px 12px #030506, -4px -4px 12px #292a30; */
|
|
||||||
/* border: 5px solid blue */
|
|
||||||
border-radius: 10px;
|
|
||||||
/* background: #16171a; */
|
|
||||||
}
|
|
||||||
|
|
||||||
#chat-zone .message-item.moderator .message-bloc,
|
|
||||||
#chat-zone .message-item.customer .message-bloc {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compose {
|
|
||||||
position: absolute;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
left: 10%;
|
|
||||||
right: initial;
|
|
||||||
/* bottom: calc(3rem + 2rem); */
|
|
||||||
bottom: 0;
|
|
||||||
width: 80%;
|
|
||||||
height: 2rem;
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
padding: 0;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compose input {
|
|
||||||
width: 90%;
|
|
||||||
height: 2rem;
|
|
||||||
line-height: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.compose input::placeholder {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Hide video controls on mobile*/
|
|
||||||
/*todo: still buggy on iOS, play/pause button pops up on load for iOS,
|
|
||||||
goes away after you press pause then play again*/
|
|
||||||
video ::-webkit-media-controls-panel {
|
|
||||||
display: none !important;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
video ::--webkit-media-controls-play-button {
|
|
||||||
display: none !important;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
video ::-webkit-media-controls-start-playback-button {
|
|
||||||
display: none !important;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* End mobile layout */
|
|
@ -1,27 +0,0 @@
|
|||||||
@media (max-width: 640px) {
|
|
||||||
.section:first-of-type {
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
section:first-of-type .hero-inner {
|
|
||||||
padding-top: 1rem;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
section:first-of-type .hero-inner .split-item {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
section:nth-of-type(2) {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
section:nth-of-type(2) .section-inner {
|
|
||||||
padding-top: 1rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
section:nth-of-type(2) .section-inner .cta-slogan {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
.snackbar-container {
|
|
||||||
box-shadow: 9px 9px 16px #0a0b0c, -9px -9px 16px #222328;
|
|
||||||
transition: all 0.5s ease;
|
|
||||||
transition-property: top, right, bottom, left, opacity;
|
|
||||||
font-family: "Heebo", sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
min-height: 14px;
|
|
||||||
position: fixed;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
color: #b4b4b4;
|
|
||||||
line-height: 22px;
|
|
||||||
padding: 18px 24px;
|
|
||||||
bottom: -100px;
|
|
||||||
top: -100px;
|
|
||||||
opacity: 0;
|
|
||||||
z-index: 9999;
|
|
||||||
|
|
||||||
background-color: #376df9;
|
|
||||||
background: linear-gradient(100deg, #376df9 0, #ff5fa0 75%, #ffc55a 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackbar-container .action {
|
|
||||||
background: white;
|
|
||||||
padding: 7px;
|
|
||||||
border-radius: 3px;
|
|
||||||
display: inline-block;
|
|
||||||
border: none;
|
|
||||||
font-size: inherit;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: #000000;
|
|
||||||
margin: 0 0 0 24px;
|
|
||||||
/*padding: 0;*/
|
|
||||||
min-width: min-content;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
|
||||||
.snackbar-container {
|
|
||||||
min-width: 288px;
|
|
||||||
max-width: 568px;
|
|
||||||
display: inline-flex;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @media (max-width: 640px) {
|
|
||||||
.snackbar-container {
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
.snackbar-pos.bottom-center {
|
|
||||||
top: auto !important;
|
|
||||||
bottom: 0;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackbar-pos.bottom-left {
|
|
||||||
top: auto !important;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackbar-pos.bottom-right {
|
|
||||||
top: auto !important;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackbar-pos.top-left {
|
|
||||||
bottom: auto !important;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackbar-pos.top-center {
|
|
||||||
bottom: auto !important;
|
|
||||||
top: 0;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackbar-pos.top-right {
|
|
||||||
bottom: auto !important;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile Styles Start */
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.snackbar-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 5rem;
|
|
||||||
bottom: initial;
|
|
||||||
left: calc(10vw - 0.5rem);
|
|
||||||
right: initial;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 80vw !important;
|
|
||||||
min-width: 80vw;
|
|
||||||
max-width: 80vw;
|
|
||||||
height: auto;
|
|
||||||
margin: 0;
|
|
||||||
margin-top: 1rem;
|
|
||||||
padding: 0.5rem;
|
|
||||||
word-wrap: break-word;
|
|
||||||
line-height: 1.2rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackbar-container p {
|
|
||||||
width: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackbar-container .action {
|
|
||||||
width: 15%;
|
|
||||||
padding: 0.2rem;
|
|
||||||
border-radius: 10px;
|
|
||||||
display: inline-block;
|
|
||||||
border: none;
|
|
||||||
font-size: 1rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: #000000;
|
|
||||||
margin: 0;
|
|
||||||
/*padding: 0;*/
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snackbar-pos.bottom-center,
|
|
||||||
.snackbar-pos.top-center {
|
|
||||||
left: calc(10vw - 0.5rem);
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile Styles End */
|
|
@ -1 +0,0 @@
|
|||||||
<svg width="124" height="24" xmlns="http://www.w3.org/2000/svg"><path d="M63.849 11.694c-1.023 0-1.76.326-2.507.657v7.507c.716.066 1.126.066 1.805.066 2.454 0 2.79-1.092 2.79-2.616v-3.585c0-1.125-.384-2.029-2.088-2.029zm-16.295-.41c-1.702 0-2.09.908-2.09 2.033v.631h4.179v-.631c0-1.125-.389-2.033-2.089-2.033zm-31.566 7.813c0 .89.432 1.352 1.386 1.352 1.022 0 1.628-.324 2.375-.657v-1.78h-2.237c-1.059 0-1.524.19-1.524 1.085zM79.7 11.694c-1.705 0-2.296.904-2.296 2.03v4.106c0 1.129.59 2.035 2.296 2.035 1.7 0 2.296-.906 2.296-2.035v-4.107c0-1.125-.596-2.029-2.296-2.029zM7.632 23.702H2.619V11.917H.115v-4.06H2.62v-2.44C2.62 2.104 4.033.134 8.05.134h3.345v4.062h-2.09c-1.565 0-1.668.568-1.668 1.627l-.006 2.033h3.787l-.443 4.06H7.632v11.786zm17.13.031h-4.177l-.18-1.026a9.802 9.802 0 01-4.734 1.192c-3.063 0-4.694-1.988-4.694-4.737 0-3.244 1.903-4.402 5.307-4.402h3.465v-.701c0-1.656-.196-2.142-2.817-2.142h-4.286l.419-4.06h4.685c5.751 0 7.012 1.764 7.012 6.235v9.641zm14.206-11.518c-2.6-.433-3.346-.528-4.597-.528-2.246 0-2.925.481-2.925 2.335v3.506c0 1.853.679 2.337 2.925 2.337 1.251 0 1.998-.097 4.597-.532v3.961c-2.277.496-3.76.627-5.014.627-5.381 0-7.52-2.75-7.52-6.72v-2.845c0-3.975 2.139-6.729 7.52-6.729 1.254 0 2.737.13 5.014.629v3.959zM54.654 17.2h-9.191v.328c0 1.853.68 2.337 2.925 2.337 2.02 0 3.252-.097 5.847-.532v3.961c-2.503.496-3.807.627-6.262.627-5.382 0-7.522-2.75-7.522-6.72v-3.253c0-3.475 1.587-6.32 7.102-6.32 5.515 0 7.101 2.812 7.101 6.32V17.2zm16.294.076c0 3.838-1.129 6.637-7.97 6.637-2.471 0-3.92-.21-6.647-.618V1.355l5.01-.813v7.675c1.084-.39 2.485-.59 3.76-.59 5.012 0 5.847 2.183 5.847 5.69v3.959zm16.063.083c0 3.311-1.407 6.523-7.295 6.523-5.891 0-7.325-3.212-7.325-6.523v-3.197c0-3.313 1.434-6.525 7.325-6.525 5.888 0 7.295 3.212 7.295 6.525v3.197zm16.052 0c0 3.311-1.41 6.523-7.296 6.523-5.891 0-7.325-3.212-7.325-6.523v-3.197c0-3.313 1.434-6.525 7.325-6.525 5.887 0 7.296 3.212 7.296 6.525v3.197zm16.472 6.343h-5.431l-4.594-7.448v7.448h-5.012V1.354l5.012-.812v14.387l4.594-7.073h5.431l-5.014 7.719 5.014 8.127zM95.75 11.694c-1.703 0-2.294.904-2.294 2.03v4.106c0 1.129.591 2.035 2.294 2.035 1.7 0 2.301-.906 2.301-2.035v-4.107c0-1.125-.601-2.029-2.301-2.029zm26.646 9.229c.844 0 1.516.668 1.516 1.503 0 .848-.672 1.51-1.522 1.51a1.511 1.511 0 01-1.532-1.51c0-.835.686-1.503 1.532-1.503h.006zm-.006.234c-.68 0-1.237.568-1.237 1.27 0 .713.557 1.274 1.243 1.274.687.007 1.235-.56 1.235-1.268s-.548-1.276-1.235-1.276h-.006zm-.288 2.144h-.275v-1.677c.144-.02.282-.04.488-.04.261 0 .432.054.537.127.101.074.156.187.156.347 0 .221-.15.354-.336.408v.013c.151.027.254.16.289.406.04.261.082.361.108.416h-.288c-.04-.055-.082-.208-.117-.429-.04-.213-.15-.293-.37-.293h-.192v.722zm0-.928h.2c.225 0 .417-.081.417-.289 0-.147-.11-.293-.418-.293-.09 0-.152.007-.2.013v.569z" fill="#6A6F82"/></svg>
|
|
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 6.3 KiB |
@ -1 +0,0 @@
|
|||||||
<svg width="125" height="39" xmlns="http://www.w3.org/2000/svg"><path d="M65.879 9.8a2.533 2.533 0 01-2.539 2.537 2.532 2.532 0 01-2.538-2.538 2.508 2.508 0 012.538-2.537c1.446.039 2.539 1.171 2.539 2.537zm-10.466 5.114v.624s-1.21-1.562-3.787-1.562c-4.256 0-7.576 3.24-7.576 7.73 0 4.45 3.28 7.73 7.576 7.73 2.616 0 3.787-1.601 3.787-1.601v.663c0 .313.235.546.547.546h3.163V14.365H55.96a.561.561 0 00-.547.549zm0 9.407c-.585.86-1.757 1.601-3.162 1.601-2.5 0-4.413-1.561-4.413-4.216 0-2.655 1.914-4.216 4.413-4.216 1.367 0 2.616.78 3.162 1.6v5.231zm6.053-9.954h3.749v14.678h-3.749V14.367zm55.998-.391c-2.578 0-3.788 1.562-3.788 1.562V7.301h-3.749v21.744h3.163a.558.558 0 00.547-.546v-.664s1.21 1.6 3.787 1.6c4.257 0 7.576-3.277 7.576-7.728 0-4.45-3.319-7.731-7.536-7.731zm-.625 11.907c-1.445 0-2.577-.741-3.163-1.6V19.05c.586-.78 1.835-1.6 3.163-1.6 2.499 0 4.412 1.561 4.412 4.216 0 2.654-1.913 4.216-4.412 4.216zm-8.864-5.543v8.744h-3.75V20.77c0-2.42-.78-3.396-2.888-3.396-1.132 0-2.304.585-3.047 1.445v10.228h-3.748v-14.68h2.967c.313 0 .547.274.547.548v.624c1.094-1.132 2.538-1.562 3.983-1.562 1.64 0 3.007.47 4.1 1.406 1.328 1.093 1.836 2.498 1.836 4.958zm-22.533-6.364c-2.576 0-3.787 1.562-3.787 1.562V7.301h-3.749v21.744h3.163a.559.559 0 00.547-.546v-.664s1.21 1.6 3.787 1.6c4.257 0 7.576-3.277 7.576-7.728.04-4.451-3.28-7.731-7.537-7.731zm-.625 11.907c-1.444 0-2.576-.741-3.162-1.6V19.05c.586-.78 1.835-1.6 3.162-1.6 2.5 0 4.413 1.561 4.413 4.216 0 2.654-1.913 4.216-4.413 4.216zM74.665 13.976c1.132 0 1.718.196 1.718.196v3.474s-3.124-1.055-5.076 1.171v10.267h-3.75V14.367h3.164c.312 0 .546.273.546.546v.625c.704-.82 2.227-1.562 3.398-1.562zM35.733 27.718c-.195-.468-.39-.976-.586-1.406-.313-.702-.625-1.366-.898-1.99l-.039-.04a406.922 406.922 0 00-8.63-17.644l-.117-.235c-.32-.608-.633-1.22-.937-1.835-.39-.703-.78-1.444-1.406-2.147C21.87.859 20.074 0 18.161 0c-1.953 0-3.71.86-4.998 2.342-.586.703-1.016 1.444-1.406 2.148a84.724 84.724 0 01-.936 1.835l-.118.234c-3.007 5.856-5.935 11.79-8.63 17.645l-.04.078c-.272.625-.585 1.289-.898 1.99-.195.43-.39.899-.585 1.406-.508 1.444-.664 2.81-.468 4.217a8.297 8.297 0 005.076 6.48c1.016.43 2.07.625 3.163.625.313 0 .703-.039 1.016-.078 1.288-.156 2.616-.585 3.905-1.327 1.6-.898 3.124-2.186 4.842-4.06 1.718 1.874 3.28 3.162 4.842 4.06 1.29.742 2.616 1.17 3.905 1.327.312.04.703.078 1.016.078 1.093 0 2.186-.195 3.162-.625 2.734-1.094 4.647-3.591 5.077-6.48.31-1.366.154-2.732-.353-4.177zm-17.611 2.03c-2.11-2.655-3.476-5.153-3.944-7.26-.195-.899-.235-1.68-.117-2.382a3.78 3.78 0 01.625-1.64c.742-1.054 1.991-1.718 3.436-1.718 1.445 0 2.734.625 3.437 1.718.312.468.547 1.015.625 1.64.117.703.078 1.522-.117 2.381-.47 2.069-1.837 4.568-3.945 7.26zm15.58 1.835a5.802 5.802 0 01-3.553 4.568c-.937.39-1.953.507-2.968.39-.976-.118-1.953-.43-2.967-1.015-1.406-.782-2.812-1.991-4.452-3.787 2.577-3.162 4.139-6.051 4.725-8.627a9.765 9.765 0 00.195-3.32 6.329 6.329 0 00-1.054-2.654c-1.212-1.757-3.242-2.771-5.507-2.771-2.264 0-4.295 1.054-5.505 2.771a6.335 6.335 0 00-1.055 2.655 8.107 8.107 0 00.195 3.319c.586 2.576 2.187 5.504 4.725 8.666-1.601 1.796-3.046 3.006-4.452 3.787-1.015.586-1.991.898-2.967 1.015a6.25 6.25 0 01-2.968-.39 5.802 5.802 0 01-3.553-4.568 6.457 6.457 0 01.351-3.045c.117-.39.313-.78.508-1.25.273-.624.585-1.288.898-1.951l.04-.078a425.627 425.627 0 018.59-17.528l.117-.235c.313-.585.625-1.21.937-1.795.313-.625.664-1.211 1.094-1.719.82-.936 1.913-1.444 3.124-1.444 1.21 0 2.304.508 3.124 1.444.43.51.78 1.095 1.093 1.719.313.585.626 1.21.937 1.795l.118.235a516.84 516.84 0 018.552 17.567v.039c.312.626.586 1.328.898 1.953.195.468.39.858.508 1.248.311 1.014.428 1.991.272 3.006z" fill="#6A6F82"/></svg>
|
|
Before Width: | Height: | Size: 3.6 KiB |
@ -1 +0,0 @@
|
|||||||
<svg width="150" height="31" xmlns="http://www.w3.org/2000/svg"><g fill="#6A6F82" fill-rule="evenodd"><path d="M150 14.514v-2.647h-3.295V7.75l-.11.034-3.095.945-.061.019v3.118h-4.884V10.13c0-.81.181-1.428.538-1.841.355-.408.863-.615 1.51-.615.465 0 .947.11 1.431.325l.122.054V5.265l-.057-.021c-.452-.162-1.068-.244-1.83-.244-.96 0-1.834.209-2.596.622a4.432 4.432 0 00-1.78 1.757c-.419.751-.631 1.618-.631 2.578v1.91h-2.294v2.647h2.294v11.153h3.293V14.514h4.884v7.088c0 2.919 1.38 4.398 4.1 4.398a6.78 6.78 0 001.4-.155c.488-.105.822-.21 1.018-.322l.043-.026v-2.672l-.134.089a2.309 2.309 0 01-.662.288 2.52 2.52 0 01-.65.11c-.638 0-1.11-.171-1.402-.51-.296-.34-.446-.938-.446-1.773v-6.515H150zm-24.387 8.799c-1.195 0-2.137-.396-2.801-1.175-.669-.783-1.007-1.9-1.007-3.317 0-1.464.338-2.61 1.007-3.406.664-.791 1.598-1.193 2.775-1.193 1.142 0 2.05.383 2.702 1.14.654.762.986 1.898.986 3.379 0 1.498-.312 2.65-.928 3.42-.612.764-1.531 1.152-2.734 1.152zm.147-11.779c-2.28 0-4.092.667-5.383 1.982-1.291 1.315-1.945 3.136-1.945 5.41 0 2.161.638 3.9 1.898 5.165 1.26 1.267 2.975 1.908 5.096 1.908 2.21 0 3.986-.676 5.277-2.009 1.29-1.332 1.945-3.135 1.945-5.356 0-2.195-.614-3.946-1.825-5.204-1.211-1.258-2.915-1.896-5.063-1.896zm-12.638 0c-1.551 0-2.834.396-3.815 1.177-.986.785-1.486 1.815-1.486 3.062 0 .647.108 1.223.32 1.711.214.49.545.921.985 1.283.436.359 1.11.735 2.001 1.117.75.308 1.31.569 1.665.774.347.201.594.404.733.6.135.193.204.457.204.783 0 .927-.696 1.378-2.128 1.378-.53 0-1.136-.11-1.8-.329a6.769 6.769 0 01-1.844-.932l-.136-.098v3.164l.05.023c.466.215 1.053.396 1.746.538a9.423 9.423 0 001.864.215c1.684 0 3.04-.398 4.028-1.183.996-.79 1.5-1.845 1.5-3.135 0-.93-.271-1.728-.807-2.37-.531-.639-1.454-1.225-2.74-1.743-1.026-.41-1.683-.751-1.954-1.013-.261-.253-.394-.61-.394-1.063 0-.401.164-.723.5-.983.339-.262.81-.395 1.401-.395.55 0 1.11.087 1.669.256a5.4 5.4 0 011.457.674l.134.092v-3.001l-.051-.022c-.378-.162-.875-.3-1.48-.412a9.05 9.05 0 00-1.622-.168zM99.236 23.313c-1.195 0-2.138-.396-2.802-1.175-.668-.783-1.006-1.899-1.006-3.317 0-1.464.338-2.61 1.007-3.406.664-.791 1.597-1.193 2.774-1.193 1.142 0 2.05.383 2.702 1.14.655.762.987 1.898.987 3.379 0 1.498-.313 2.65-.929 3.42-.611.764-1.53 1.152-2.733 1.152zm.147-11.779c-2.281 0-4.093.667-5.384 1.982-1.29 1.315-1.945 3.136-1.945 5.41 0 2.162.64 3.9 1.9 5.165C95.213 25.358 96.927 26 99.048 26c2.21 0 3.986-.676 5.277-2.009 1.29-1.332 1.945-3.135 1.945-5.356 0-2.195-.614-3.946-1.825-5.204-1.212-1.258-2.916-1.896-5.063-1.896zm-12.328 2.723v-2.39h-3.253v13.8h3.253v-7.06c0-1.2.273-2.186.811-2.93.531-.737 1.24-1.11 2.104-1.11.293 0 .622.049.978.144.353.095.608.198.759.306l.136.099v-3.273l-.052-.022c-.303-.129-.732-.194-1.274-.194-.818 0-1.55.263-2.176.779-.55.453-.947 1.075-1.251 1.85h-.035zm-9.079-2.723c-1.492 0-2.823.32-3.955.95a6.4 6.4 0 00-2.61 2.676c-.594 1.143-.896 2.478-.896 3.966 0 1.304.293 2.5.871 3.555a6.114 6.114 0 002.435 2.456c1.035.573 2.231.863 3.556.863 1.546 0 2.866-.309 3.924-.917l.043-.024v-2.974l-.137.1a6.12 6.12 0 01-1.591.826c-.575.2-1.1.302-1.56.302-1.276 0-2.3-.399-3.044-1.185-.746-.786-1.123-1.891-1.123-3.281 0-1.4.394-2.533 1.17-3.369.775-.833 1.802-1.256 3.052-1.256 1.069 0 2.11.361 3.096 1.075l.137.098v-3.133l-.044-.025c-.371-.207-.877-.378-1.505-.508a9.008 9.008 0 00-1.819-.195zm-9.701.333h-3.253v13.8h3.253v-13.8zm-1.593-5.879c-.536 0-1.003.182-1.386.542a1.787 1.787 0 00-.581 1.354c0 .529.193.975.575 1.327.379.351.847.529 1.392.529a2.01 2.01 0 001.398-.528 1.73 1.73 0 00.582-1.328c0-.518-.19-.969-.566-1.339-.375-.37-.851-.557-1.414-.557zm-8.117 4.86v14.819h3.32V6.41H57.29l-5.84 14.302L45.782 6.41H41v19.256h3.12v-14.82h.107l5.985 14.82h2.354l5.892-14.818h.107z" fill-rule="nonzero"/><path d="M15 14H0V0h15zm17 0H17V0h15zM15 31H0V17h15zm17 0H17V17h15z"/></g></svg>
|
|
Before Width: | Height: | Size: 3.7 KiB |
@ -1 +0,0 @@
|
|||||||
<svg width="113" height="30" xmlns="http://www.w3.org/2000/svg"><g fill="#6A6F82"><path d="M0 5h3.94v8.21h8.31V5h3.946v20.153H12.25V16.77H3.94v8.383H0V5zm28.678 13.589c0 1.687-1.465 3.06-3.264 3.06-1.799 0-3.264-1.373-3.264-3.06V9.912h-3.736v8.677c0 3.62 3.14 6.564 7 6.564s7-2.945 7-6.564V9.912h-3.736v8.677zm14.167-8.575c-1.854 0-3.147.504-4.397 1.655V5H34.7v12.359c0 4.626 3.567 7.794 7.573 7.794 4.454 0 8.357-3.225 8.357-7.57 0-4.29-3.599-7.57-7.784-7.57zm.093 11.886c-2.54 0-4.483-1.99-4.483-4.317s1.943-4.317 4.483-4.317c2.152 0 4.096 1.99 4.096 4.317S45.09 21.9 42.938 21.9zm13.987-11c0-1.768 1.256-2.328 2.63-2.328 1.107 0 2.572.785 3.527 1.738l2.451-2.69c-1.225-1.543-3.705-2.608-5.738-2.608-4.066 0-6.995 2.215-6.995 5.888 0 6.812 8.938 4.652 8.938 8.466 0 1.176-1.225 2.214-2.63 2.214-2.212 0-2.93-1.009-3.946-2.074l-2.72 2.634c1.734 1.991 3.886 3 6.457 3 3.856 0 6.964-2.242 6.964-5.747 0-7.569-8.938-5.214-8.938-8.493zm18.808-1.084c-4.007 0-7.573 3.167-7.573 7.794V30h3.748v-6.7c1.25 1.15 2.543 1.655 4.397 1.655 4.185 0 7.785-3.28 7.785-7.57 0-4.345-3.904-7.569-8.357-7.569zm.666 11.887c-2.54 0-4.483-1.992-4.483-4.318 0-2.327 1.943-4.317 4.483-4.317 2.152 0 4.095 1.99 4.095 4.317 0 2.326-1.943 4.318-4.095 4.318zm35.285.197c-2.211 0-2.84-.897-2.84-2.27v-6.084h3.438v-3.083h-3.437V6.397l-3.795 1.598v12.391c0 3.168 2.33 4.767 5.527 4.767.479 0 1.137-.03 1.496-.112l.927-3.196c-.418.027-.897.055-1.316.055z"/><path d="M100.964 13.775c-.714-1.105-1.718-1.99-2.974-2.658a8.595 8.595 0 00-3.009-.937V6.67c1.097-.42 1.778-1.346 1.778-2.428 0-1.474-1.315-2.668-2.952-2.668-1.639 0-2.976 1.194-2.976 2.668 0 1.082.641 2.009 1.738 2.428v3.513a9.297 9.297 0 00-2.618.738c-1.697-1.16-7.262-4.962-10.517-7.183.078-.25.137-.509.137-.781C79.57 1.323 78.1 0 76.285 0S73 1.323 73 2.956c0 1.632 1.47 2.956 3.287 2.956a3.53 3.53 0 001.686-.43l.687.468 9.44 6.117c-.5.412-.964.88-1.336 1.407-.753 1.072-1.214 2.252-1.214 3.538v.269c0 .903.191 1.756.516 2.557a6.56 6.56 0 001.223 1.91l-3.133 2.825c-.927-.31-1.97-.104-2.668.527a2.16 2.16 0 00-.743 1.615c0 .61.264 1.184.744 1.615a2.67 2.67 0 001.796.67 2.674 2.674 0 001.797-.67 2.16 2.16 0 00.743-1.615c0-.236-.04-.466-.117-.685l3.238-2.913c.444.276.924.508 1.44.708a9.221 9.221 0 003.366.637h.225a8.651 8.651 0 003.842-.874c1.269-.62 2.262-1.468 3.012-2.55.754-1.086 1.169-2.285 1.169-3.604v-.066c0-1.298-.334-2.496-1.036-3.593zm-3.956 6.11c-.879.879-1.89 1.42-3.031 1.42h-.188c-.653 0-1.29-.162-1.915-.457a4.292 4.292 0 01-1.678-1.374c-.452-.576-.698-1.205-.698-1.87V17.4c0-.655.14-1.277.492-1.863A4.23 4.23 0 0191.552 14a4.133 4.133 0 012.163-.587h.074c.716 0 1.394.127 2.034.422a4.181 4.181 0 011.598 1.307c.401.565.64 1.174.717 1.837.012.138.018.28.018.414 0 .9-.383 1.735-1.148 2.492z"/></g></svg>
|
|
Before Width: | Height: | Size: 2.7 KiB |
@ -1 +0,0 @@
|
|||||||
<svg width="72" height="72" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-63.9%" y="-63.9%" width="227.8%" height="227.8%" filterUnits="objectBoundingBox" id="a"><feOffset dy="8" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="8" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0862745098 0 0 0 0 0.0901960784 0 0 0 0 0.105882353 0 0 0 0.32 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" fill="#FFF"><path d="M28 25h19l-3 6H25z"/><path opacity=".64" d="M28 33h19l-3 6H25z"/><path d="M28 41h19l-3 6H25z"/></g></svg>
|
|
Before Width: | Height: | Size: 718 B |
@ -1 +0,0 @@
|
|||||||
<svg width="72" height="72" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-63.9%" y="-63.9%" width="227.8%" height="227.8%" filterUnits="objectBoundingBox" id="a"><feOffset dy="8" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="8" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0862745098 0 0 0 0 0.0901960784 0 0 0 0 0.105882353 0 0 0 0.32 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" fill="#FFF"><rect x="24" y="24" width="14" height="17" rx="1"/><path d="M46.859 31.168L40.3 29.393l-.522 1.931 5.6 1.514-3.399 12.548-11.584-3.131-.521 1.931 12.55 3.393a1 1 0 001.226-.705l3.913-14.48a1 1 0 00-.704-1.226z" opacity=".64"/></g></svg>
|
|
Before Width: | Height: | Size: 840 B |
@ -1 +0,0 @@
|
|||||||
<svg width="72" height="72" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-63.9%" y="-63.9%" width="227.8%" height="227.8%" filterUnits="objectBoundingBox" id="a"><feOffset dy="8" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="8" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0862745098 0 0 0 0 0.0901960784 0 0 0 0 0.105882353 0 0 0 0.32 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" fill="#FFF"><path d="M33 34h-7a1 1 0 01-1-1v-7a1 1 0 011-1h7a1 1 0 011 1v7a1 1 0 01-1 1zm0 13h-7a1 1 0 01-1-1v-7a1 1 0 011-1h7a1 1 0 011 1v7a1 1 0 01-1 1zm4-21h10v2H37z"/><path opacity=".64" d="M37 31h10v2H37zm0 8h10v2H37z"/><path d="M37 44h10v2H37z"/></g></svg>
|
|
Before Width: | Height: | Size: 854 B |
@ -1 +0,0 @@
|
|||||||
<svg width="72" height="72" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-63.9%" y="-63.9%" width="227.8%" height="227.8%" filterUnits="objectBoundingBox" id="a"><feOffset dy="8" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="8" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0862745098 0 0 0 0 0.0901960784 0 0 0 0 0.105882353 0 0 0 0.32 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" fill="#FFF"><path d="M48 36a2.99 2.99 0 00-2-2.816v-3.368A2.993 2.993 0 1042.184 26h-3.368a2.982 2.982 0 00-5.632 0h-3.368A2.993 2.993 0 1026 29.816v3.368a2.982 2.982 0 000 5.632v3.368A2.993 2.993 0 1029.816 46h3.368a2.982 2.982 0 005.632 0h3.368A2.993 2.993 0 1046 42.184v-3.368A2.99 2.99 0 0048 36zm-4 2.816v3.368c-.848.3-1.515.968-1.816 1.816h-3.368a2.982 2.982 0 00-5.632 0h-3.368A2.987 2.987 0 0028 42.184v-3.368a2.982 2.982 0 000-5.632v-3.368A2.987 2.987 0 0029.816 28h3.368a2.982 2.982 0 005.632 0h3.368c.3.848.968 1.515 1.816 1.816v3.368a2.982 2.982 0 000 5.632z"/><circle opacity=".64" cx="36" cy="36" r="3"/></g></svg>
|
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1 +0,0 @@
|
|||||||
<svg width="72" height="72" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-63.9%" y="-63.9%" width="227.8%" height="227.8%" filterUnits="objectBoundingBox" id="a"><feOffset dy="8" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="8" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0862745098 0 0 0 0 0.0901960784 0 0 0 0 0.105882353 0 0 0 0.32 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" fill="#FFF"><path d="M35.406 24.132l-10.82 5.055a.42.42 0 00.001.764l10.905 5.018c.322.148.694.148 1.016 0l10.905-5.018a.42.42 0 00.002-.764l-10.821-5.055a1.403 1.403 0 00-1.188 0z"/><path d="M47.415 35.676l-2.235-1.044a1.216 1.216 0 00-1.023-.003l-7.649 3.52a1.216 1.216 0 01-1.016 0l-7.65-3.52a1.216 1.216 0 00-1.022.003l-2.235 1.044a.42.42 0 00.002.764l10.905 5.017a1.21 1.21 0 001.016 0l10.905-5.017a.42.42 0 00.002-.764z" opacity=".64"/><path d="M47.415 42.108l-2.235-1.044a1.216 1.216 0 00-1.023-.003l-7.649 3.519a1.216 1.216 0 01-1.016 0l-7.65-3.52a1.216 1.216 0 00-1.022.003l-2.235 1.045a.42.42 0 00.002.763l10.905 5.018c.322.148.694.148 1.016 0l10.905-5.018a.42.42 0 00.002-.763z"/></g></svg>
|
|
Before Width: | Height: | Size: 1.3 KiB |
@ -1 +0,0 @@
|
|||||||
<svg width="72" height="72" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-63.9%" y="-63.9%" width="227.8%" height="227.8%" filterUnits="objectBoundingBox" id="a"><feOffset dy="8" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="8" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0862745098 0 0 0 0 0.0901960784 0 0 0 0 0.105882353 0 0 0 0.32 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" fill="#FFF"><path d="M33 47c.008-7.729 6.271-13.992 14-14 .444.004.888.03 1.329.08a11.993 11.993 0 10-15.248 15.249c-.05-.441-.077-.885-.081-1.329z"/><path d="M47 35a11.92 11.92 0 00-11.844 13.844l13.688-13.688c-.61-.1-1.226-.151-1.844-.156z" opacity=".64"/></g></svg>
|
|
Before Width: | Height: | Size: 860 B |
Before Width: | Height: | Size: 151 KiB |
Before Width: | Height: | Size: 151 KiB |
Before Width: | Height: | Size: 215 KiB |
Before Width: | Height: | Size: 215 KiB |
Before Width: | Height: | Size: 187 KiB |
Before Width: | Height: | Size: 183 KiB |
@ -1 +0,0 @@
|
|||||||
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><radialGradient cx="44.286%" cy="100%" fx="44.286%" fy="100%" r="120.709%" gradientTransform="matrix(0 -.82759 1 0 -.557 1.367)" id="a"><stop stop-color="#376DF9" offset="0%"/><stop stop-color="#D262B4" offset="46.208%"/><stop stop-color="#FF8D74" offset="77.058%"/><stop stop-color="#FFD4CA" offset="100%"/></radialGradient><radialGradient cx="44.286%" cy="0%" fx="44.286%" fy="0%" r="120.709%" gradientTransform="matrix(0 .82759 -1 0 .443 -.367)" id="c"><stop stop-color="#376DF9" offset="0%"/><stop stop-color="#D262B4" offset="46.208%"/><stop stop-color="#FF8D74" offset="77.058%"/><stop stop-color="#FFD4CA" offset="100%"/></radialGradient><path d="M20 29c8-6.915 12-12.582 12-17 0-6.627-5.373-12-12-12S8 5.373 8 12c0 4.418 4 10.085 12 17z" id="b"/><path d="M12 32c8-6.915 12-12.582 12-17 0-6.627-5.373-12-12-12S0 8.373 0 15c0 4.418 4 10.085 12 17z" id="d"/></defs><g fill="none" fill-rule="evenodd"><use fill="url(#a)" opacity=".88" xlink:href="#b"/><use fill="url(#c)" opacity=".48" transform="matrix(1 0 0 -1 0 35)" xlink:href="#d"/></g></svg>
|
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 8.4 KiB |
@ -1 +0,0 @@
|
|||||||
<svg width="712" height="400" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="e"><stop stop-color="#FFF" stop-opacity=".32" offset="0%"/><stop stop-color="#FFF" stop-opacity="0" offset="100%"/></linearGradient><linearGradient x1="77.999%" y1="9.763%" x2="24.434%" y2="90.469%" id="h"><stop stop-color="#12141D" stop-opacity=".72" offset="0%"/><stop stop-color="#12141D" offset="100%"/></linearGradient><filter x="-70%" y="-50%" width="240%" height="240%" filterUnits="objectBoundingBox" id="f"><feOffset dy="16" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="16" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.0862745098 0 0 0 0 0.0901960784 0 0 0 0 0.105882353 0 0 0 0.24 0" in="shadowBlurOuter1"/></filter><filter x="-466.7%" y="-250%" width="1033.3%" height="800%" filterUnits="objectBoundingBox" id="i"><feOffset dy="16" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="16" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0.062745098 0 0 0 0 0.0862745098 0 0 0 0 0.254901961 0 0 0 0.24 0" in="shadowBlurOuter1"/></filter><rect id="a" x="0" y="0" width="712" height="400" rx="2"/><rect id="b" x="0" y="0" width="712" height="400" rx="2"/><radialGradient cx="14.004%" cy="20.714%" fx="14.004%" fy="20.714%" r="204.167%" gradientTransform="matrix(.4898 .4898 -.1074 .34029 .094 .068)" id="c"><stop stop-color="#376DF9" offset="0%"/><stop stop-color="#FF5FA6" offset="61.018%"/><stop stop-color="#FFC55A" stop-opacity="0" offset="100%"/></radialGradient><circle id="g" cx="40" cy="40" r="40"/><path d="M48 40a.999.999 0 00-.427-.82l-10-7A1 1 0 0036 33v14a.999.999 0 001.573.82l10-7A.995.995 0 0048 40c0 .001 0 .001 0 0z" id="j"/></defs><g fill="none" fill-rule="evenodd"><use fill="#292B32" xlink:href="#a"/><mask id="d" fill="#fff"><use xlink:href="#b"/></mask><use fill="url(#c)" xlink:href="#b"/><g mask="url(#d)" stroke="url(#e)" stroke-width="1.5"><path d="M332.302 106c42.692 0 77.302 34.609 77.302 77.302 0 42.693-34.61 77.302-77.302 77.302-42.693 0-77.302-34.609-77.302-77.302C255 140.609 289.609 106 332.302 106z"/><path d="M336.986 109.306c42.692 0 77.302 34.609 77.302 77.302 0 42.693-34.61 77.302-77.302 77.302-42.693 0-77.302-34.61-77.302-77.302 0-42.693 34.61-77.302 77.302-77.302z"/><path d="M341.672 112.614c42.693 0 77.302 34.609 77.302 77.302 0 42.693-34.61 77.302-77.302 77.302-42.692 0-77.302-34.61-77.302-77.302 0-42.693 34.61-77.302 77.302-77.302z"/><path d="M346.356 115.92c42.693 0 77.302 34.609 77.302 77.302 0 42.693-34.61 77.302-77.302 77.302-42.692 0-77.302-34.61-77.302-77.302 0-42.693 34.61-77.302 77.302-77.302z"/><path d="M351.042 119.228c42.692 0 77.302 34.609 77.302 77.302 0 42.693-34.61 77.302-77.302 77.302-42.692 0-77.302-34.609-77.302-77.302 0-42.693 34.61-77.302 77.302-77.302z"/><path d="M355.726 122.534c42.693 0 77.302 34.609 77.302 77.302 0 42.693-34.61 77.302-77.302 77.302-42.693 0-77.302-34.61-77.302-77.302 0-42.693 34.609-77.302 77.302-77.302z"/><path d="M360.412 125.842c42.692 0 77.302 34.609 77.302 77.302 0 42.693-34.61 77.302-77.302 77.302-42.693 0-77.302-34.61-77.302-77.302 0-42.693 34.609-77.302 77.302-77.302z"/><path d="M365.096 129.148c42.693 0 77.302 34.609 77.302 77.302 0 42.693-34.609 77.302-77.302 77.302-42.693 0-77.302-34.609-77.302-77.302 0-42.693 34.609-77.302 77.302-77.302z"/><path d="M369.782 132.456c42.692 0 77.302 34.61 77.302 77.302 0 42.693-34.61 77.302-77.302 77.302-42.693 0-77.302-34.609-77.302-77.302 0-42.693 34.609-77.302 77.302-77.302z"/><path d="M374.466 135.762c42.693 0 77.302 34.609 77.302 77.302 0 42.693-34.609 77.302-77.302 77.302-42.693 0-77.302-34.61-77.302-77.302 0-42.693 34.61-77.302 77.302-77.302z"/><path d="M379.152 139.07c42.692 0 77.302 34.609 77.302 77.302 0 42.693-34.61 77.302-77.302 77.302-42.693 0-77.302-34.61-77.302-77.302 0-42.693 34.61-77.302 77.302-77.302z"/></g><g transform="translate(316 160)"><use fill="#000" filter="url(#f)" xlink:href="#g"/><use fill="url(#h)" xlink:href="#g"/></g><g transform="translate(316 160)"><use fill="#000" filter="url(#i)" xlink:href="#j"/><use fill="#6991FA" xlink:href="#j"/></g></g></svg>
|
|
Before Width: | Height: | Size: 4.2 KiB |
@ -1,28 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const accordionEl = document.getElementsByClassName("accordion-header");
|
|
||||||
|
|
||||||
function openAccordion(parent, panel) {
|
|
||||||
parent.classList.add("is-active");
|
|
||||||
panel.style.maxHeight = panel.scrollHeight + "px";
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeAccordion(parent, panel) {
|
|
||||||
parent.classList.remove("is-active");
|
|
||||||
panel.style.maxHeight = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accordionEl.length > 0) {
|
|
||||||
for (let i = 0; i < accordionEl.length; i++) {
|
|
||||||
const el = accordionEl[i];
|
|
||||||
const parent = el.parentNode;
|
|
||||||
const panel = el.nextElementSibling;
|
|
||||||
parent.classList.contains("is-active") && openAccordion(parent, panel);
|
|
||||||
el.addEventListener("click", function () {
|
|
||||||
parent.classList.contains("is-active")
|
|
||||||
? closeAccordion(parent, panel)
|
|
||||||
: openAccordion(parent, panel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,42 +0,0 @@
|
|||||||
// Generated by CoffeeScript 1.10.0
|
|
||||||
(function () {
|
|
||||||
var autoLink,
|
|
||||||
slice = [].slice;
|
|
||||||
|
|
||||||
autoLink = function () {
|
|
||||||
var callback, k, linkAttributes, option, options, pattern, v;
|
|
||||||
options = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
|
||||||
pattern = /(^|[\s\n]|<[A-Za-z]*\/?>)((?:https?|ftp):\/\/[\-A-Z0-9+\u0026\u2019@#\/%?=()~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~()_|])/gi;
|
|
||||||
if (!(options.length > 0)) {
|
|
||||||
return this.replace(pattern, "$1<a target='_blank' href='$2'>$2</a>");
|
|
||||||
}
|
|
||||||
option = options[0];
|
|
||||||
callback = option["callback"];
|
|
||||||
linkAttributes = (function () {
|
|
||||||
var results;
|
|
||||||
results = [];
|
|
||||||
for (k in option) {
|
|
||||||
v = option[k];
|
|
||||||
if (k !== "callback") {
|
|
||||||
results.push(" " + k + "='" + v + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
})().join("");
|
|
||||||
return this.replace(pattern, function (match, space, url) {
|
|
||||||
var link;
|
|
||||||
link =
|
|
||||||
(typeof callback === "function" ? callback(url) : void 0) ||
|
|
||||||
"<a target='_blank' href='" +
|
|
||||||
url +
|
|
||||||
"'" +
|
|
||||||
linkAttributes +
|
|
||||||
">" +
|
|
||||||
url +
|
|
||||||
"</a>";
|
|
||||||
return "" + space + link;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
String.prototype["autoLink"] = autoLink;
|
|
||||||
}.call(this));
|
|
@ -1,195 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// Swipe detector
|
|
||||||
// https://gist.github.com/chrishaensel/e17c9f3838f246d75fe3bd19d6bb92e8#file-swipe-js
|
|
||||||
let swipe = {
|
|
||||||
touchStartX: 0,
|
|
||||||
touchEndX: 0,
|
|
||||||
minSwipePixels: 30,
|
|
||||||
detectionZone: undefined,
|
|
||||||
swipeCallback: function () {},
|
|
||||||
|
|
||||||
init: function (detectionZone, callback) {
|
|
||||||
swipe.swipeCallback = callback;
|
|
||||||
detectionZone.addEventListener(
|
|
||||||
"touchstart",
|
|
||||||
function (event) {
|
|
||||||
swipe.touchStartX = event.changedTouches[0].screenX;
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
detectionZone.addEventListener(
|
|
||||||
"touchend",
|
|
||||||
function (event) {
|
|
||||||
swipe.touchEndX = event.changedTouches[0].screenX;
|
|
||||||
swipe.handleSwipeGesture();
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleSwipeGesture: function () {
|
|
||||||
let direction, moved;
|
|
||||||
if (swipe.touchEndX <= swipe.touchStartX) {
|
|
||||||
moved = swipe.touchStartX - swipe.touchEndX;
|
|
||||||
direction = "left";
|
|
||||||
}
|
|
||||||
if (swipe.touchEndX >= swipe.touchStartX) {
|
|
||||||
moved = swipe.touchEndX - swipe.touchStartX;
|
|
||||||
direction = "right";
|
|
||||||
}
|
|
||||||
if (moved > swipe.minSwipePixels && direction !== "undefined") {
|
|
||||||
swipe.swipe(direction, moved);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
swipe: function (direction, movedPixels) {
|
|
||||||
let ret = {};
|
|
||||||
ret.direction = direction;
|
|
||||||
ret.movedPixels = movedPixels;
|
|
||||||
swipe.swipeCallback(ret);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const carousels = document.getElementsByClassName("carousel-items");
|
|
||||||
|
|
||||||
// Rotate the carousel forward or backward
|
|
||||||
function rotateCarousel(el, dir) {
|
|
||||||
if (dir === undefined) {
|
|
||||||
dir = "next";
|
|
||||||
}
|
|
||||||
let currentItem = el.getElementsByClassName("carousel-item is-active")[0];
|
|
||||||
let nextItem =
|
|
||||||
dir === "next"
|
|
||||||
? currentItem.nextElementSibling
|
|
||||||
: currentItem.previousElementSibling;
|
|
||||||
let index = currentItem.getAttribute("data-carousel");
|
|
||||||
let currentBullet = el.parentNode.getElementsByClassName("carousel-bullet")[
|
|
||||||
index
|
|
||||||
];
|
|
||||||
let nextBullet =
|
|
||||||
dir === "next"
|
|
||||||
? currentBullet.nextElementSibling
|
|
||||||
: currentBullet.previousElementSibling;
|
|
||||||
currentItem.classList.remove("is-active");
|
|
||||||
currentBullet.classList.remove("is-active");
|
|
||||||
if (nextItem) {
|
|
||||||
nextItem.classList.add("is-active");
|
|
||||||
nextBullet.classList.add("is-active");
|
|
||||||
} else {
|
|
||||||
if (dir === "next") {
|
|
||||||
el.firstElementChild.classList.add("is-active");
|
|
||||||
el.parentNode
|
|
||||||
.getElementsByClassName("carousel-bullets")[0]
|
|
||||||
.firstElementChild.classList.add("is-active");
|
|
||||||
} else {
|
|
||||||
el.lastElementChild.classList.add("is-active");
|
|
||||||
el.parentNode
|
|
||||||
.getElementsByClassName("carousel-bullets")[0]
|
|
||||||
.lastElementChild.classList.add("is-active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal heights fix
|
|
||||||
function equalHeightCarousel(carousel, items) {
|
|
||||||
let taller = 0;
|
|
||||||
let height;
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
items[0].parentNode.style.minHeight = taller + "px";
|
|
||||||
items[i].classList.add("is-loading");
|
|
||||||
height = items[i].offsetHeight;
|
|
||||||
items[i].classList.remove("is-loading");
|
|
||||||
if (height > taller) {
|
|
||||||
taller = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
items[0].parentNode.style.minHeight = taller + "px";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear autorotate
|
|
||||||
function clearAutorotate(autorotate) {
|
|
||||||
if (autorotate) {
|
|
||||||
clearInterval(autorotate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (carousels.length > 0) {
|
|
||||||
for (let i = 0; i < carousels.length; i++) {
|
|
||||||
let carousel = carousels[i];
|
|
||||||
let items = carousel.getElementsByClassName("carousel-item");
|
|
||||||
let activeItem = 0;
|
|
||||||
let autorotateTiming = carousel.getAttribute("data-autorotate");
|
|
||||||
// Generate bullets container
|
|
||||||
const bulletsContainer = document.createElement("div");
|
|
||||||
bulletsContainer.className = "carousel-bullets";
|
|
||||||
carousel.parentNode.insertBefore(bulletsContainer, carousel.nextSibling);
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
// Add data attributes
|
|
||||||
items[i].setAttribute("data-carousel", i);
|
|
||||||
// Determine a new active item, if any
|
|
||||||
if (items[i].classList.contains("is-active")) activeItem = i;
|
|
||||||
// Generate bullets
|
|
||||||
let bullet = document.createElement("button");
|
|
||||||
bullet.className = "carousel-bullet";
|
|
||||||
bullet.setAttribute("data-bullet", i);
|
|
||||||
carousel.parentNode
|
|
||||||
.getElementsByClassName("carousel-bullets")[0]
|
|
||||||
.appendChild(bullet);
|
|
||||||
}
|
|
||||||
// Add is-active class to first carousel item and bullet
|
|
||||||
items[activeItem].classList.add("is-active");
|
|
||||||
let bullets = carousel.parentNode.getElementsByClassName(
|
|
||||||
"carousel-bullet"
|
|
||||||
);
|
|
||||||
bullets[activeItem].classList.add("is-active");
|
|
||||||
// Equal height items
|
|
||||||
equalHeightCarousel(carousel, items);
|
|
||||||
window.addEventListener("resize", function () {
|
|
||||||
equalHeightCarousel(carousel, items);
|
|
||||||
});
|
|
||||||
// Autorotate
|
|
||||||
let autorotate = false;
|
|
||||||
if (autorotateTiming) {
|
|
||||||
autorotate = setInterval(function () {
|
|
||||||
rotateCarousel(carousel, "next");
|
|
||||||
}, autorotateTiming);
|
|
||||||
}
|
|
||||||
// Rotate by bullet click
|
|
||||||
for (let i = 0; i < bullets.length; i++) {
|
|
||||||
let bullet = bullets[i];
|
|
||||||
bullet.addEventListener("click", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
// Do nothing if item is active
|
|
||||||
if (bullet.classList.contains("is-active")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Remove active classes
|
|
||||||
for (let i = 0; i < bullets.length; i++) {
|
|
||||||
bullets[i].classList.remove("is-active");
|
|
||||||
}
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
items[i].classList.remove("is-active");
|
|
||||||
}
|
|
||||||
// Add active classes to corresponding items and bullets
|
|
||||||
let index = this.getAttribute("data-bullet");
|
|
||||||
items[index].classList.add("is-active");
|
|
||||||
this.classList.add("is-active");
|
|
||||||
// Clear autorotate timing
|
|
||||||
clearAutorotate(autorotate);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Rotate on swipe
|
|
||||||
swipe.init(carousel, function (e) {
|
|
||||||
if (e.direction === "left") {
|
|
||||||
rotateCarousel(carousel, "next");
|
|
||||||
} else if (e.direction === "right") {
|
|
||||||
rotateCarousel(carousel, "prev");
|
|
||||||
}
|
|
||||||
// Clear autorotate timing
|
|
||||||
clearAutorotate(autorotate);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,944 +0,0 @@
|
|||||||
// Vars
|
|
||||||
var isMuted;
|
|
||||||
var videoIsPaused;
|
|
||||||
var dataChanel = null;
|
|
||||||
const browserName = getBrowserName();
|
|
||||||
const url = window.location.href;
|
|
||||||
const roomHash = url.substring(url.lastIndexOf("/") + 1).toLowerCase();
|
|
||||||
var mode = "camera";
|
|
||||||
// var isFullscreen = false;
|
|
||||||
var sendingCaptions = false;
|
|
||||||
var receivingCaptions = false;
|
|
||||||
const isWebRTCSupported =
|
|
||||||
navigator.getUserMedia ||
|
|
||||||
navigator.webkitGetUserMedia ||
|
|
||||||
navigator.mozGetUserMedia ||
|
|
||||||
navigator.msGetUserMedia ||
|
|
||||||
window.RTCPeerConnection;
|
|
||||||
|
|
||||||
// Element vars
|
|
||||||
const chatInput = document.querySelector(".compose input");
|
|
||||||
const remoteVideoVanilla = document.getElementById("remote-video");
|
|
||||||
const remoteVideo = $("#remote-video");
|
|
||||||
const captionText = $("#remote-video-text");
|
|
||||||
const localVideoText = $("#local-video-text");
|
|
||||||
const captionButtontext = $("#caption-button-text");
|
|
||||||
const entireChat = $("#entire-chat");
|
|
||||||
const chatZone = $("#chat-zone");
|
|
||||||
|
|
||||||
var VideoChat = {
|
|
||||||
connected: false,
|
|
||||||
willInitiateCall: false,
|
|
||||||
localICECandidates: [],
|
|
||||||
socket: io(),
|
|
||||||
remoteVideo: document.getElementById("remote-video"),
|
|
||||||
localVideo: document.getElementById("local-video"),
|
|
||||||
recognition: undefined,
|
|
||||||
|
|
||||||
// Call to getUserMedia (provided by adapter.js for cross browser compatibility)
|
|
||||||
// asking for access to both the video and audio streams. If the request is
|
|
||||||
// accepted callback to the onMediaStream function, otherwise callback to the
|
|
||||||
// noMediaStream function.
|
|
||||||
requestMediaStream: function (event) {
|
|
||||||
logIt("requestMediaStream");
|
|
||||||
rePositionLocalVideo();
|
|
||||||
navigator.mediaDevices
|
|
||||||
.getUserMedia({
|
|
||||||
video: true,
|
|
||||||
audio: true,
|
|
||||||
})
|
|
||||||
.then((stream) => {
|
|
||||||
VideoChat.onMediaStream(stream);
|
|
||||||
localVideoText.text("Drag Me");
|
|
||||||
setTimeout(() => localVideoText.fadeOut(), 5000);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
logIt(error);
|
|
||||||
logIt(
|
|
||||||
"Failed to get local webcam video, check webcam privacy settings"
|
|
||||||
);
|
|
||||||
// Keep trying to get user media
|
|
||||||
setTimeout(VideoChat.requestMediaStream, 1000);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Called when a video stream is added to VideoChat
|
|
||||||
onMediaStream: function (stream) {
|
|
||||||
logIt("onMediaStream");
|
|
||||||
VideoChat.localStream = stream;
|
|
||||||
// Add the stream as video's srcObject.
|
|
||||||
// Now that we have webcam video sorted, prompt user to share URL
|
|
||||||
Snackbar.show({
|
|
||||||
text: "Here is the join link for your call: " + url,
|
|
||||||
actionText: "Copy Link",
|
|
||||||
width: "750px",
|
|
||||||
pos: "top-center",
|
|
||||||
actionTextColor: "#616161",
|
|
||||||
duration: 500000,
|
|
||||||
backgroundColor: "#16171a",
|
|
||||||
onActionClick: function (element) {
|
|
||||||
// Copy url to clipboard, this is achieved by creating a temporary element,
|
|
||||||
// adding the text we want to that element, selecting it, then deleting it
|
|
||||||
var copyContent = window.location.href;
|
|
||||||
$('<input id="some-element">')
|
|
||||||
.val(copyContent)
|
|
||||||
.appendTo("body")
|
|
||||||
.select();
|
|
||||||
document.execCommand("copy");
|
|
||||||
var toRemove = document.querySelector("#some-element");
|
|
||||||
toRemove.parentNode.removeChild(toRemove);
|
|
||||||
Snackbar.close();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
VideoChat.localVideo.srcObject = stream;
|
|
||||||
// Now we're ready to join the chat room.
|
|
||||||
VideoChat.socket.emit("join", roomHash);
|
|
||||||
// Add listeners to the websocket
|
|
||||||
VideoChat.socket.on("full", chatRoomFull);
|
|
||||||
VideoChat.socket.on("offer", VideoChat.onOffer);
|
|
||||||
VideoChat.socket.on("ready", VideoChat.readyToCall);
|
|
||||||
VideoChat.socket.on(
|
|
||||||
"willInitiateCall",
|
|
||||||
() => (VideoChat.willInitiateCall = true)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// When we are ready to call, enable the Call button.
|
|
||||||
readyToCall: function (event) {
|
|
||||||
logIt("readyToCall");
|
|
||||||
// First to join call will most likely initiate call
|
|
||||||
if (VideoChat.willInitiateCall) {
|
|
||||||
logIt("Initiating call");
|
|
||||||
VideoChat.startCall();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Set up a callback to run when we have the ephemeral token to use Twilio's TURN server.
|
|
||||||
startCall: function (event) {
|
|
||||||
logIt("startCall >>> Sending token request...");
|
|
||||||
VideoChat.socket.on("token", VideoChat.onToken(VideoChat.createOffer));
|
|
||||||
VideoChat.socket.emit("token", roomHash);
|
|
||||||
},
|
|
||||||
|
|
||||||
// When we receive the ephemeral token back from the server.
|
|
||||||
onToken: function (callback) {
|
|
||||||
logIt("onToken");
|
|
||||||
return function (token) {
|
|
||||||
logIt("<<< Received token");
|
|
||||||
// Set up a new RTCPeerConnection using the token's iceServers.
|
|
||||||
VideoChat.peerConnection = new RTCPeerConnection({
|
|
||||||
iceServers: token.iceServers,
|
|
||||||
});
|
|
||||||
// Add the local video stream to the peerConnection.
|
|
||||||
VideoChat.localStream.getTracks().forEach(function (track) {
|
|
||||||
VideoChat.peerConnection.addTrack(track, VideoChat.localStream);
|
|
||||||
});
|
|
||||||
// Add general purpose data channel to peer connection,
|
|
||||||
// used for text chats, captions, and toggling sending captions
|
|
||||||
dataChanel = VideoChat.peerConnection.createDataChannel("chat", {
|
|
||||||
negotiated: true,
|
|
||||||
// both peers must have same id
|
|
||||||
id: 0,
|
|
||||||
});
|
|
||||||
// Called when dataChannel is successfully opened
|
|
||||||
dataChanel.onopen = function (event) {
|
|
||||||
logIt("dataChannel opened");
|
|
||||||
};
|
|
||||||
// Handle different dataChannel types
|
|
||||||
dataChanel.onmessage = function (event) {
|
|
||||||
const receivedData = event.data;
|
|
||||||
// First 4 chars represent data type
|
|
||||||
const dataType = receivedData.substring(0, 4);
|
|
||||||
const cleanedMessage = receivedData.slice(4);
|
|
||||||
if (dataType === "mes:") {
|
|
||||||
handleRecieveMessage(cleanedMessage);
|
|
||||||
} else if (dataType === "cap:") {
|
|
||||||
recieveCaptions(cleanedMessage);
|
|
||||||
} else if (dataType === "tog:") {
|
|
||||||
toggleSendCaptions();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Set up callbacks for the connection generating iceCandidates or
|
|
||||||
// receiving the remote media stream.
|
|
||||||
VideoChat.peerConnection.onicecandidate = VideoChat.onIceCandidate;
|
|
||||||
VideoChat.peerConnection.onaddstream = VideoChat.onAddStream;
|
|
||||||
// Set up listeners on the socket
|
|
||||||
VideoChat.socket.on("candidate", VideoChat.onCandidate);
|
|
||||||
VideoChat.socket.on("answer", VideoChat.onAnswer);
|
|
||||||
VideoChat.socket.on("requestToggleCaptions", () => toggleSendCaptions());
|
|
||||||
VideoChat.socket.on("recieveCaptions", (captions) =>
|
|
||||||
recieveCaptions(captions)
|
|
||||||
);
|
|
||||||
// Called when there is a change in connection state
|
|
||||||
VideoChat.peerConnection.oniceconnectionstatechange = function (event) {
|
|
||||||
switch (VideoChat.peerConnection.iceConnectionState) {
|
|
||||||
case "connected":
|
|
||||||
logIt("connected");
|
|
||||||
// Once connected we no longer have a need for the signaling server, so disconnect
|
|
||||||
VideoChat.socket.disconnect();
|
|
||||||
break;
|
|
||||||
case "disconnected":
|
|
||||||
logIt("disconnected");
|
|
||||||
case "failed":
|
|
||||||
logIt("failed");
|
|
||||||
// VideoChat.socket.connect
|
|
||||||
// VideoChat.createOffer();
|
|
||||||
// Refresh page if connection has failed
|
|
||||||
location.reload();
|
|
||||||
break;
|
|
||||||
case "closed":
|
|
||||||
logIt("closed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// When the peerConnection generates an ice candidate, send it over the socket to the peer.
|
|
||||||
onIceCandidate: function (event) {
|
|
||||||
logIt("onIceCandidate");
|
|
||||||
if (event.candidate) {
|
|
||||||
logIt(
|
|
||||||
`<<< Received local ICE candidate from STUN/TURN server (${event.candidate.address})`
|
|
||||||
);
|
|
||||||
if (VideoChat.connected) {
|
|
||||||
logIt(`>>> Sending local ICE candidate (${event.candidate.address})`);
|
|
||||||
VideoChat.socket.emit(
|
|
||||||
"candidate",
|
|
||||||
JSON.stringify(event.candidate),
|
|
||||||
roomHash
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// If we are not 'connected' to the other peer, we are buffering the local ICE candidates.
|
|
||||||
// This most likely is happening on the "caller" side.
|
|
||||||
// The peer may not have created the RTCPeerConnection yet, so we are waiting for the 'answer'
|
|
||||||
// to arrive. This will signal that the peer is ready to receive signaling.
|
|
||||||
VideoChat.localICECandidates.push(event.candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// When receiving a candidate over the socket, turn it back into a real
|
|
||||||
// RTCIceCandidate and add it to the peerConnection.
|
|
||||||
onCandidate: function (candidate) {
|
|
||||||
// Update caption text
|
|
||||||
captionText.text("Found other user... connecting");
|
|
||||||
rtcCandidate = new RTCIceCandidate(JSON.parse(candidate));
|
|
||||||
logIt(
|
|
||||||
`onCandidate <<< Received remote ICE candidate (${rtcCandidate.address} - ${rtcCandidate.relatedAddress})`
|
|
||||||
);
|
|
||||||
VideoChat.peerConnection.addIceCandidate(rtcCandidate);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Create an offer that contains the media capabilities of the browser.
|
|
||||||
createOffer: function () {
|
|
||||||
logIt("createOffer >>> Creating offer...");
|
|
||||||
VideoChat.peerConnection.createOffer(
|
|
||||||
function (offer) {
|
|
||||||
// If the offer is created successfully, set it as the local description
|
|
||||||
// and send it over the socket connection to initiate the peerConnection
|
|
||||||
// on the other side.
|
|
||||||
VideoChat.peerConnection.setLocalDescription(offer);
|
|
||||||
VideoChat.socket.emit("offer", JSON.stringify(offer), roomHash);
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
logIt("failed offer creation");
|
|
||||||
logIt(err, true);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Create an answer with the media capabilities that both browsers share.
|
|
||||||
// This function is called with the offer from the originating browser, which
|
|
||||||
// needs to be parsed into an RTCSessionDescription and added as the remote
|
|
||||||
// description to the peerConnection object. Then the answer is created in the
|
|
||||||
// same manner as the offer and sent over the socket.
|
|
||||||
createAnswer: function (offer) {
|
|
||||||
logIt("createAnswer");
|
|
||||||
return function () {
|
|
||||||
logIt(">>> Creating answer...");
|
|
||||||
rtcOffer = new RTCSessionDescription(JSON.parse(offer));
|
|
||||||
VideoChat.peerConnection.setRemoteDescription(rtcOffer);
|
|
||||||
VideoChat.peerConnection.createAnswer(
|
|
||||||
function (answer) {
|
|
||||||
VideoChat.peerConnection.setLocalDescription(answer);
|
|
||||||
VideoChat.socket.emit("answer", JSON.stringify(answer), roomHash);
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
logIt("Failed answer creation.");
|
|
||||||
logIt(err, true);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// When a browser receives an offer, set up a callback to be run when the
|
|
||||||
// ephemeral token is returned from Twilio.
|
|
||||||
onOffer: function (offer) {
|
|
||||||
logIt("onOffer <<< Received offer");
|
|
||||||
VideoChat.socket.on(
|
|
||||||
"token",
|
|
||||||
VideoChat.onToken(VideoChat.createAnswer(offer))
|
|
||||||
);
|
|
||||||
VideoChat.socket.emit("token", roomHash);
|
|
||||||
},
|
|
||||||
|
|
||||||
// When an answer is received, add it to the peerConnection as the remote description.
|
|
||||||
onAnswer: function (answer) {
|
|
||||||
logIt("onAnswer <<< Received answer");
|
|
||||||
var rtcAnswer = new RTCSessionDescription(JSON.parse(answer));
|
|
||||||
// Set remote description of RTCSession
|
|
||||||
VideoChat.peerConnection.setRemoteDescription(rtcAnswer);
|
|
||||||
// The caller now knows that the callee is ready to accept new ICE candidates, so sending the buffer over
|
|
||||||
VideoChat.localICECandidates.forEach((candidate) => {
|
|
||||||
logIt(`>>> Sending local ICE candidate (${candidate.address})`);
|
|
||||||
// Send ice candidate over websocket
|
|
||||||
VideoChat.socket.emit("candidate", JSON.stringify(candidate), roomHash);
|
|
||||||
});
|
|
||||||
// Reset the buffer of local ICE candidates. This is not really needed, but it's good practice
|
|
||||||
VideoChat.localICECandidates = [];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Called when a stream is added to the peer connection
|
|
||||||
onAddStream: function (event) {
|
|
||||||
logIt("onAddStream <<< Received new stream from remote. Adding it...");
|
|
||||||
// Update remote video source
|
|
||||||
VideoChat.remoteVideo.srcObject = event.stream;
|
|
||||||
// Close the initial share url snackbar
|
|
||||||
Snackbar.close();
|
|
||||||
// Remove the loading gif from video
|
|
||||||
VideoChat.remoteVideo.style.background = "none";
|
|
||||||
// Update connection status
|
|
||||||
VideoChat.connected = true;
|
|
||||||
// Hide caption status text
|
|
||||||
captionText.fadeOut();
|
|
||||||
// Reposition local video after a second, as there is often a delay
|
|
||||||
// between adding a stream and the height of the video div changing
|
|
||||||
setTimeout(() => rePositionLocalVideo(), 500);
|
|
||||||
// var timesRun = 0;
|
|
||||||
// var interval = setInterval(function () {
|
|
||||||
// timesRun += 1;
|
|
||||||
// if (timesRun === 10) {
|
|
||||||
// clearInterval(interval);
|
|
||||||
// }
|
|
||||||
// rePositionLocalVideo();
|
|
||||||
// }, 300);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get name of browser session using user agent
|
|
||||||
function getBrowserName() {
|
|
||||||
var name = "Unknown";
|
|
||||||
if (window.navigator.userAgent.indexOf("MSIE") !== -1) {
|
|
||||||
} else if (window.navigator.userAgent.indexOf("Firefox") !== -1) {
|
|
||||||
name = "Firefox";
|
|
||||||
} else if (window.navigator.userAgent.indexOf("Opera") !== -1) {
|
|
||||||
name = "Opera";
|
|
||||||
} else if (window.navigator.userAgent.indexOf("Chrome") !== -1) {
|
|
||||||
name = "Chrome";
|
|
||||||
} else if (window.navigator.userAgent.indexOf("Safari") !== -1) {
|
|
||||||
name = "Safari";
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic logging class wrapper
|
|
||||||
function logIt(message, error) {
|
|
||||||
console.log(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when socket receives message that room is full
|
|
||||||
function chatRoomFull() {
|
|
||||||
alert(
|
|
||||||
"Chat room is full. Check to make sure you don't have multiple open tabs, or try with a new room link"
|
|
||||||
);
|
|
||||||
// Exit room and redirect
|
|
||||||
window.location.href = "/newcall";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reposition local video to top left of remote video
|
|
||||||
function rePositionLocalVideo() {
|
|
||||||
// Get position of remote video
|
|
||||||
var bounds = remoteVideo.position();
|
|
||||||
let localVideo = $("#local-video");
|
|
||||||
if (
|
|
||||||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
||||||
navigator.userAgent
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
bounds.top = $(window).height() * 0.7;
|
|
||||||
bounds.left += 10;
|
|
||||||
} else {
|
|
||||||
bounds.top += 10;
|
|
||||||
bounds.left += 10;
|
|
||||||
}
|
|
||||||
// Set position of local video
|
|
||||||
$("#moveable").css(bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reposition captions to bottom of video
|
|
||||||
function rePositionCaptions() {
|
|
||||||
// Get remote video position
|
|
||||||
var bounds = remoteVideo.position();
|
|
||||||
bounds.top -= 10;
|
|
||||||
bounds.top = bounds.top + remoteVideo.height() - 1 * captionText.height();
|
|
||||||
// Reposition captions
|
|
||||||
captionText.css(bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when window is resized
|
|
||||||
function windowResized() {
|
|
||||||
rePositionLocalVideo();
|
|
||||||
rePositionCaptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fullscreen
|
|
||||||
// function openFullscreen() {
|
|
||||||
// try {
|
|
||||||
// // var elem = document.getElementById("remote-video");
|
|
||||||
// var elem = document.getElementById("body");
|
|
||||||
// if (!isFullscreen) {
|
|
||||||
// VideoChat.remoteVideo.classList.add("fullscreen");
|
|
||||||
// isFullscreen = true;
|
|
||||||
// if (elem.requestFullscreen) {
|
|
||||||
// elem.requestFullscreen();
|
|
||||||
// } else if (elem.mozRequestFullScreen) {
|
|
||||||
// /* Firefox */
|
|
||||||
// elem.mozRequestFullScreen();
|
|
||||||
// } else if (elem.webkitRequestFullscreen) {
|
|
||||||
// /* Chrome, Safari and Opera */
|
|
||||||
//
|
|
||||||
// elem.webkitRequestFullscreen();
|
|
||||||
// setTimeout(windowResized, 1000);
|
|
||||||
// } else if (elem.msRequestFullscreen) {
|
|
||||||
// /* IE/Edge */
|
|
||||||
// elem.msRequestFullscreen();
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// isFullscreen = false;
|
|
||||||
// VideoChat.remoteVideo.classList.remove("fullscreen");
|
|
||||||
// if (document.exitFullscreen) {
|
|
||||||
// document.exitFullscreen();
|
|
||||||
// } else if (document.mozCancelFullScreen) {
|
|
||||||
// /* Firefox */
|
|
||||||
// document.mozCancelFullScreen();
|
|
||||||
// } else if (document.webkitExitFullscreen) {
|
|
||||||
// /* Chrome, Safari and Opera */
|
|
||||||
// document.webkitExitFullscreen();
|
|
||||||
// } else if (document.msExitFullscreen) {
|
|
||||||
// /* IE/Edge */
|
|
||||||
// document.msExitFullscreen();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// logIt(e);
|
|
||||||
// }
|
|
||||||
// setTimeout(windowResized, 1000);
|
|
||||||
// }
|
|
||||||
// End Fullscreen
|
|
||||||
|
|
||||||
// Mute microphone
|
|
||||||
function muteMicrophone() {
|
|
||||||
var audioTrack = null;
|
|
||||||
// Get audio track to mute
|
|
||||||
VideoChat.peerConnection.getSenders().find(function (s) {
|
|
||||||
if (s.track.kind === "audio") {
|
|
||||||
audioTrack = s.track;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
isMuted = !audioTrack.enabled;
|
|
||||||
audioTrack.enabled = isMuted;
|
|
||||||
isMuted = !isMuted;
|
|
||||||
// select mic button and mic button text
|
|
||||||
const micButtonIcon = document.getElementById("mic-icon");
|
|
||||||
const micButtonText = document.getElementById("mic-text");
|
|
||||||
// Update mute button text and icon
|
|
||||||
if (isMuted) {
|
|
||||||
micButtonIcon.classList.remove("fa-microphone");
|
|
||||||
micButtonIcon.classList.add("fa-microphone-slash");
|
|
||||||
micButtonText.innerText = "Unmute";
|
|
||||||
} else {
|
|
||||||
micButtonIcon.classList.add("fa-microphone");
|
|
||||||
micButtonIcon.classList.remove("fa-microphone-slash");
|
|
||||||
micButtonText.innerText = "Mute";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// End Mute microphone
|
|
||||||
|
|
||||||
// Pause Video
|
|
||||||
function pauseVideo() {
|
|
||||||
var videoTrack = null;
|
|
||||||
// Get video track to pause
|
|
||||||
VideoChat.peerConnection.getSenders().find(function (s) {
|
|
||||||
if (s.track.kind === "video") {
|
|
||||||
videoTrack = s.track;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
videoIsPaused = !videoTrack.enabled;
|
|
||||||
videoTrack.enabled = videoIsPaused;
|
|
||||||
videoIsPaused = !videoIsPaused;
|
|
||||||
// select video button and video button text
|
|
||||||
const videoButtonIcon = document.getElementById("video-icon");
|
|
||||||
const videoButtonText = document.getElementById("video-text");
|
|
||||||
// update pause button icon and text
|
|
||||||
if (videoIsPaused) {
|
|
||||||
localVideoText.text("Video is paused");
|
|
||||||
localVideoText.show();
|
|
||||||
videoButtonIcon.classList.remove("fa-video");
|
|
||||||
videoButtonIcon.classList.add("fa-video-slash");
|
|
||||||
videoButtonText.innerText = "Unpause Video";
|
|
||||||
} else {
|
|
||||||
localVideoText.text("Video unpaused");
|
|
||||||
setTimeout(() => localVideoText.fadeOut(), 2000);
|
|
||||||
videoButtonIcon.classList.add("fa-video");
|
|
||||||
videoButtonIcon.classList.remove("fa-video-slash");
|
|
||||||
videoButtonText.innerText = "Pause Video";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// End pause Video
|
|
||||||
|
|
||||||
// Swap camera / screen share
|
|
||||||
function swap() {
|
|
||||||
// Handle swap video before video call is connected
|
|
||||||
if (!VideoChat.connected) {
|
|
||||||
alert("You must join a call before you can share your screen.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Store swap button icon and text
|
|
||||||
const swapIcon = document.getElementById("swap-icon");
|
|
||||||
const swapText = document.getElementById("swap-text");
|
|
||||||
// If mode is camera then switch to screen share
|
|
||||||
if (mode === "camera") {
|
|
||||||
// Show accept screenshare snackbar
|
|
||||||
Snackbar.show({
|
|
||||||
text:
|
|
||||||
"Please allow screen share. Click the middle of the picture above and then press share.",
|
|
||||||
width: "400px",
|
|
||||||
pos: "bottom-center",
|
|
||||||
actionTextColor: "#616161",
|
|
||||||
duration: 50000,
|
|
||||||
});
|
|
||||||
// Request screen share, note we dont want to capture audio
|
|
||||||
// as we already have the stream from the Webcam
|
|
||||||
navigator.mediaDevices
|
|
||||||
.getDisplayMedia({
|
|
||||||
video: true,
|
|
||||||
audio: false,
|
|
||||||
})
|
|
||||||
.then(function (stream) {
|
|
||||||
// Close allow screenshare snackbar
|
|
||||||
Snackbar.close();
|
|
||||||
// Change display mode
|
|
||||||
mode = "screen";
|
|
||||||
// Update swap button icon and text
|
|
||||||
swapIcon.classList.remove("fa-desktop");
|
|
||||||
swapIcon.classList.add("fa-camera");
|
|
||||||
swapText.innerText = "Share Webcam";
|
|
||||||
switchStreamHelper(stream);
|
|
||||||
})
|
|
||||||
.catch(function (err) {
|
|
||||||
logIt(err);
|
|
||||||
logIt("Error sharing screen");
|
|
||||||
Snackbar.close();
|
|
||||||
});
|
|
||||||
// If mode is screenshare then switch to webcam
|
|
||||||
} else {
|
|
||||||
// Stop the screen share track
|
|
||||||
VideoChat.localVideo.srcObject.getTracks().forEach((track) => track.stop());
|
|
||||||
// Get webcam input
|
|
||||||
navigator.mediaDevices
|
|
||||||
.getUserMedia({
|
|
||||||
video: true,
|
|
||||||
audio: true,
|
|
||||||
})
|
|
||||||
.then(function (stream) {
|
|
||||||
// Change display mode
|
|
||||||
mode = "camera";
|
|
||||||
// Update swap button icon and text
|
|
||||||
swapIcon.classList.remove("fa-camera");
|
|
||||||
swapIcon.classList.add("fa-desktop");
|
|
||||||
swapText.innerText = "Share Screen";
|
|
||||||
switchStreamHelper(stream);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap current video track with passed in stream
|
|
||||||
function switchStreamHelper(stream) {
|
|
||||||
// Get current video track
|
|
||||||
let videoTrack = stream.getVideoTracks()[0];
|
|
||||||
// Add listen for if the current track swaps, swap back
|
|
||||||
videoTrack.onended = function () {
|
|
||||||
swap();
|
|
||||||
};
|
|
||||||
if (VideoChat.connected) {
|
|
||||||
// Find sender
|
|
||||||
const sender = VideoChat.peerConnection.getSenders().find(function (s) {
|
|
||||||
// make sure tack types match
|
|
||||||
return s.track.kind === videoTrack.kind;
|
|
||||||
});
|
|
||||||
// Replace sender track
|
|
||||||
sender.replaceTrack(videoTrack);
|
|
||||||
}
|
|
||||||
// Update local video stream
|
|
||||||
VideoChat.localStream = videoTrack;
|
|
||||||
// Update local video object
|
|
||||||
VideoChat.localVideo.srcObject = stream;
|
|
||||||
// Unpause video on swap
|
|
||||||
if (videoIsPaused) {
|
|
||||||
pauseVideo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// End swap camera / screen share
|
|
||||||
|
|
||||||
// Live caption
|
|
||||||
// Request captions from other user, toggles state
|
|
||||||
function requestToggleCaptions() {
|
|
||||||
// Handle requesting captions before connected
|
|
||||||
if (!VideoChat.connected) {
|
|
||||||
alert("You must be connected to a peer to use Live Caption");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (receivingCaptions) {
|
|
||||||
captionText.text("").fadeOut();
|
|
||||||
captionButtontext.text("Start Live Caption");
|
|
||||||
receivingCaptions = false;
|
|
||||||
} else {
|
|
||||||
Snackbar.show({
|
|
||||||
text:
|
|
||||||
"This is an experimental feature. Live caption requires the other user to be using Chrome",
|
|
||||||
width: "400px",
|
|
||||||
pos: "bottom-center",
|
|
||||||
actionTextColor: "#616161",
|
|
||||||
duration: 10000,
|
|
||||||
});
|
|
||||||
captionButtontext.text("End Live Caption");
|
|
||||||
receivingCaptions = true;
|
|
||||||
}
|
|
||||||
// Send request to get captions over data channel
|
|
||||||
dataChanel.send("tog:");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start/stop sending captions to other user
|
|
||||||
function toggleSendCaptions() {
|
|
||||||
if (sendingCaptions) {
|
|
||||||
sendingCaptions = false;
|
|
||||||
VideoChat.recognition.stop();
|
|
||||||
} else {
|
|
||||||
startSpeech();
|
|
||||||
sendingCaptions = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start speech recognition
|
|
||||||
function startSpeech() {
|
|
||||||
try {
|
|
||||||
var SpeechRecognition =
|
|
||||||
window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
||||||
VideoChat.recognition = new SpeechRecognition();
|
|
||||||
// VideoChat.recognition.lang = "en";
|
|
||||||
} catch (e) {
|
|
||||||
sendingCaptions = false;
|
|
||||||
logIt(e);
|
|
||||||
logIt("error importing speech library");
|
|
||||||
// Alert other user that they cannon use live caption
|
|
||||||
dataChanel.send("cap:notusingchrome");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// recognition.maxAlternatives = 3;
|
|
||||||
VideoChat.recognition.continuous = true;
|
|
||||||
// Show results that aren't final
|
|
||||||
VideoChat.recognition.interimResults = true;
|
|
||||||
var finalTranscript;
|
|
||||||
VideoChat.recognition.onresult = (event) => {
|
|
||||||
let interimTranscript = "";
|
|
||||||
for (let i = event.resultIndex, len = event.results.length; i < len; i++) {
|
|
||||||
var transcript = event.results[i][0].transcript;
|
|
||||||
if (event.results[i].isFinal) {
|
|
||||||
finalTranscript += transcript;
|
|
||||||
} else {
|
|
||||||
interimTranscript += transcript;
|
|
||||||
var charsToKeep = interimTranscript.length % 100;
|
|
||||||
// Send captions over data chanel,
|
|
||||||
// subtracting as many complete 100 char slices from start
|
|
||||||
dataChanel.send(
|
|
||||||
"cap:" +
|
|
||||||
interimTranscript.substring(interimTranscript.length - charsToKeep)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
VideoChat.recognition.onend = function () {
|
|
||||||
logIt("on speech recording end");
|
|
||||||
// Restart speech recognition if user has not stopped it
|
|
||||||
if (sendingCaptions) {
|
|
||||||
startSpeech();
|
|
||||||
} else {
|
|
||||||
VideoChat.recognition.stop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
VideoChat.recognition.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recieve captions over datachannel
|
|
||||||
function recieveCaptions(captions) {
|
|
||||||
if (receivingCaptions) {
|
|
||||||
captionText.text("").fadeIn();
|
|
||||||
} else {
|
|
||||||
captionText.text("").fadeOut();
|
|
||||||
}
|
|
||||||
// Other user is not using chrome
|
|
||||||
if (captions === "notusingchrome") {
|
|
||||||
alert(
|
|
||||||
"Other caller must be using chrome for this feature to work. Live Caption turned off."
|
|
||||||
);
|
|
||||||
receivingCaptions = false;
|
|
||||||
captionText.text("").fadeOut();
|
|
||||||
captionButtontext.text("Start Live Caption");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
captionText.text(captions);
|
|
||||||
rePositionCaptions();
|
|
||||||
}
|
|
||||||
// End Live caption
|
|
||||||
|
|
||||||
// Translation
|
|
||||||
// function translate(text) {
|
|
||||||
// let fromLang = "en";
|
|
||||||
// let toLang = "zh";
|
|
||||||
// // let text = "hello how are you?";
|
|
||||||
// const API_KEY = "APIKEYHERE";
|
|
||||||
// let gurl = `https://translation.googleapis.com/language/translate/v2?key=${API_KEY}`;
|
|
||||||
// gurl += "&q=" + encodeURI(text);
|
|
||||||
// gurl += `&source=${fromLang}`;
|
|
||||||
// gurl += `&target=${toLang}`;
|
|
||||||
// fetch(gurl, {
|
|
||||||
// method: "GET",
|
|
||||||
// headers: {
|
|
||||||
// "Content-Type": "application/json",
|
|
||||||
// Accept: "application/json",
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// .then((res) => res.json())
|
|
||||||
// .then((response) => {
|
|
||||||
// // console.log("response from google: ", response);
|
|
||||||
// // return response["data"]["translations"][0]["translatedText"];
|
|
||||||
// logIt(response);
|
|
||||||
// var translatedText =
|
|
||||||
// response["data"]["translations"][0]["translatedText"];
|
|
||||||
// console.log(translatedText);
|
|
||||||
// dataChanel.send("cap:" + translatedText);
|
|
||||||
// })
|
|
||||||
// .catch((error) => {
|
|
||||||
// console.log("There was an error with the translation request: ", error);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// End Translation
|
|
||||||
|
|
||||||
// Text Chat
|
|
||||||
// Add text message to chat screen on page
|
|
||||||
function addMessageToScreen(msg, isOwnMessage) {
|
|
||||||
if (isOwnMessage) {
|
|
||||||
$(".chat-messages").append(
|
|
||||||
'<div class="message-item customer cssanimation fadeInBottom"><div class="message-bloc"><div class="message">' +
|
|
||||||
msg +
|
|
||||||
"</div></div></div>"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$(".chat-messages").append(
|
|
||||||
'<div class="message-item moderator cssanimation fadeInBottom"><div class="message-bloc"><div class="message">' +
|
|
||||||
msg +
|
|
||||||
"</div></div></div>"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for enter press on chat input
|
|
||||||
chatInput.addEventListener("keypress", function (event) {
|
|
||||||
if (event.keyCode === 13) {
|
|
||||||
// Prevent page refresh on enter
|
|
||||||
event.preventDefault();
|
|
||||||
var msg = chatInput.value;
|
|
||||||
// Prevent cross site scripting
|
|
||||||
msg = msg.replace(/</g, "<").replace(/>/g, ">");
|
|
||||||
// Make links clickable
|
|
||||||
msg = msg.autoLink();
|
|
||||||
// Send message over data channel
|
|
||||||
dataChanel.send("mes:" + msg);
|
|
||||||
// Add message to screen
|
|
||||||
addMessageToScreen(msg, true);
|
|
||||||
// Auto scroll chat down
|
|
||||||
chatZone.scrollTop(chatZone[0].scrollHeight);
|
|
||||||
// Clear chat input
|
|
||||||
chatInput.value = "";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Called when a message is recieved over the dataChannel
|
|
||||||
function handleRecieveMessage(msg) {
|
|
||||||
// Add message to screen
|
|
||||||
addMessageToScreen(msg, false);
|
|
||||||
// Auto scroll chat down
|
|
||||||
chatZone.scrollTop(chatZone[0].scrollHeight);
|
|
||||||
// Show chat if hidden
|
|
||||||
if (entireChat.is(":hidden")) {
|
|
||||||
toggleChat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show and hide chat
|
|
||||||
function toggleChat() {
|
|
||||||
var chatIcon = document.getElementById("chat-icon");
|
|
||||||
var chatText = $("#chat-text");
|
|
||||||
if (entireChat.is(":visible")) {
|
|
||||||
entireChat.fadeOut();
|
|
||||||
// Update show chat buttton
|
|
||||||
chatText.text("Show Chat");
|
|
||||||
chatIcon.classList.remove("fa-comment-slash");
|
|
||||||
chatIcon.classList.add("fa-comment");
|
|
||||||
} else {
|
|
||||||
entireChat.fadeIn();
|
|
||||||
// Update show chat buttton
|
|
||||||
chatText.text("Hide Chat");
|
|
||||||
chatIcon.classList.remove("fa-comment");
|
|
||||||
chatIcon.classList.add("fa-comment-slash");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// End Text chat
|
|
||||||
|
|
||||||
//Picture in picture
|
|
||||||
function togglePictureInPicture() {
|
|
||||||
if (
|
|
||||||
"pictureInPictureEnabled" in document ||
|
|
||||||
remoteVideoVanilla.webkitSetPresentationMode
|
|
||||||
) {
|
|
||||||
if (document.pictureInPictureElement) {
|
|
||||||
document.exitPictureInPicture().catch((error) => {
|
|
||||||
logIt("Error exiting pip.");
|
|
||||||
logIt(error);
|
|
||||||
});
|
|
||||||
} else if (remoteVideoVanilla.webkitPresentationMode === "inline") {
|
|
||||||
remoteVideoVanilla.webkitSetPresentationMode("picture-in-picture");
|
|
||||||
} else if (
|
|
||||||
remoteVideoVanilla.webkitPresentationMode === "picture-in-picture"
|
|
||||||
) {
|
|
||||||
remoteVideoVanilla.webkitSetPresentationMode("inline");
|
|
||||||
} else {
|
|
||||||
remoteVideoVanilla.requestPictureInPicture().catch((error) => {
|
|
||||||
alert(
|
|
||||||
"You must be connected to another person to enter picture in picture."
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
alert(
|
|
||||||
"Picture in picture is not supported in your browser. Consider using Chrome or Safari."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Picture in picture
|
|
||||||
|
|
||||||
function startUp() {
|
|
||||||
// Try and detect in-app browsers and redirect
|
|
||||||
var ua = navigator.userAgent || navigator.vendor || window.opera;
|
|
||||||
if (
|
|
||||||
DetectRTC.isMobileDevice &&
|
|
||||||
(ua.indexOf("FBAN") > -1 ||
|
|
||||||
ua.indexOf("FBAV") > -1 ||
|
|
||||||
ua.indexOf("Instagram") > -1)
|
|
||||||
) {
|
|
||||||
if (DetectRTC.osName === "iOS") {
|
|
||||||
window.location.href = "/notsupportedios";
|
|
||||||
} else {
|
|
||||||
window.location.href = "/notsupported";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect all iOS browsers that are not Safari
|
|
||||||
if (DetectRTC.isMobileDevice) {
|
|
||||||
if (DetectRTC.osName === "iOS" && !DetectRTC.browser.isSafari) {
|
|
||||||
window.location.href = "/notsupportedios";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isWebRTCSupported || browserName === "MSIE") {
|
|
||||||
window.location.href = "/notsupported";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set tab title
|
|
||||||
document.title = "Zipcall - " + url.substring(url.lastIndexOf("/") + 1);
|
|
||||||
|
|
||||||
// get webcam on load
|
|
||||||
VideoChat.requestMediaStream();
|
|
||||||
|
|
||||||
// Captions hidden by default
|
|
||||||
captionText.text("").fadeOut();
|
|
||||||
|
|
||||||
// Make local video draggable
|
|
||||||
$("#moveable").draggable({ containment: "window" });
|
|
||||||
|
|
||||||
// Hide button labels on load
|
|
||||||
$(".HoverState").hide();
|
|
||||||
|
|
||||||
// Text chat hidden by default
|
|
||||||
entireChat.hide();
|
|
||||||
|
|
||||||
// Show hide button labels on hover
|
|
||||||
$(document).ready(function () {
|
|
||||||
$(".hoverButton").mouseover(function () {
|
|
||||||
$(".HoverState").hide();
|
|
||||||
$(this).next().show();
|
|
||||||
});
|
|
||||||
$(".hoverButton").mouseout(function () {
|
|
||||||
$(".HoverState").hide();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fade out / show UI on mouse move
|
|
||||||
var timedelay = 1;
|
|
||||||
function delayCheck() {
|
|
||||||
if (timedelay === 5) {
|
|
||||||
// $(".multi-button").fadeOut();
|
|
||||||
$("#header").fadeOut();
|
|
||||||
timedelay = 1;
|
|
||||||
}
|
|
||||||
timedelay = timedelay + 1;
|
|
||||||
}
|
|
||||||
$(document).mousemove(function () {
|
|
||||||
$(".multi-button").fadeIn();
|
|
||||||
$("#header").fadeIn();
|
|
||||||
$(".multi-button").style = "";
|
|
||||||
timedelay = 1;
|
|
||||||
clearInterval(_delay);
|
|
||||||
_delay = setInterval(delayCheck, 500);
|
|
||||||
});
|
|
||||||
_delay = setInterval(delayCheck, 500);
|
|
||||||
|
|
||||||
// Show accept webcam snackbar
|
|
||||||
Snackbar.show({
|
|
||||||
text: "Please allow microphone and webcam access",
|
|
||||||
actionText: "Show Me How",
|
|
||||||
width: "455px",
|
|
||||||
pos: "top-right",
|
|
||||||
actionTextColor: "#616161",
|
|
||||||
duration: 50000,
|
|
||||||
onActionClick: function (element) {
|
|
||||||
window.open(
|
|
||||||
"https://getacclaim.zendesk.com/hc/en-us/articles/360001547832-Setting-the-default-camera-on-your-browser",
|
|
||||||
"_blank"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set caption text on start
|
|
||||||
captionText.text("Waiting for other user to join...").fadeIn();
|
|
||||||
|
|
||||||
// Reposition captions on start
|
|
||||||
rePositionCaptions();
|
|
||||||
|
|
||||||
// On change media devices refresh page and switch to system default
|
|
||||||
navigator.mediaDevices.ondevicechange = () => window.location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
startUp();
|
|
174
public/js/jquery.ui.touch-punch.js
vendored
@ -1,174 +0,0 @@
|
|||||||
/*!
|
|
||||||
* jQuery UI Touch Punch 0.2.3
|
|
||||||
*
|
|
||||||
* Copyright 2011–2014, Dave Furfero
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
*
|
|
||||||
* Depends:
|
|
||||||
* jquery.ui.widget.js
|
|
||||||
* jquery.ui.mouse.js
|
|
||||||
*/
|
|
||||||
(function ($) {
|
|
||||||
// Detect touch support
|
|
||||||
$.support.touch = "ontouchend" in document;
|
|
||||||
|
|
||||||
// Ignore browsers without touch support
|
|
||||||
if (!$.support.touch) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mouseProto = $.ui.mouse.prototype,
|
|
||||||
_mouseInit = mouseProto._mouseInit,
|
|
||||||
_mouseDestroy = mouseProto._mouseDestroy,
|
|
||||||
touchHandled;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulate a mouse event based on a corresponding touch event
|
|
||||||
* @param {Object} event A touch event
|
|
||||||
* @param {String} simulatedType The corresponding mouse event
|
|
||||||
*/
|
|
||||||
function simulateMouseEvent(event, simulatedType) {
|
|
||||||
// Ignore multi-touch events
|
|
||||||
if (event.originalEvent.touches.length > 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
var touch = event.originalEvent.changedTouches[0],
|
|
||||||
simulatedEvent = document.createEvent("MouseEvents");
|
|
||||||
|
|
||||||
// Initialize the simulated mouse event using the touch event's coordinates
|
|
||||||
simulatedEvent.initMouseEvent(
|
|
||||||
simulatedType, // type
|
|
||||||
true, // bubbles
|
|
||||||
true, // cancelable
|
|
||||||
window, // view
|
|
||||||
1, // detail
|
|
||||||
touch.screenX, // screenX
|
|
||||||
touch.screenY, // screenY
|
|
||||||
touch.clientX, // clientX
|
|
||||||
touch.clientY, // clientY
|
|
||||||
false, // ctrlKey
|
|
||||||
false, // altKey
|
|
||||||
false, // shiftKey
|
|
||||||
false, // metaKey
|
|
||||||
0, // button
|
|
||||||
null // relatedTarget
|
|
||||||
);
|
|
||||||
|
|
||||||
// Dispatch the simulated event to the target element
|
|
||||||
event.target.dispatchEvent(simulatedEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the jQuery UI widget's touchstart events
|
|
||||||
* @param {Object} event The widget element's touchstart event
|
|
||||||
*/
|
|
||||||
mouseProto._touchStart = function (event) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Ignore the event if another widget is already being handled
|
|
||||||
if (
|
|
||||||
touchHandled ||
|
|
||||||
!self._mouseCapture(event.originalEvent.changedTouches[0])
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the flag to prevent other widgets from inheriting the touch event
|
|
||||||
touchHandled = true;
|
|
||||||
|
|
||||||
// Track movement to determine if interaction was a click
|
|
||||||
self._touchMoved = false;
|
|
||||||
|
|
||||||
// Simulate the mouseover event
|
|
||||||
simulateMouseEvent(event, "mouseover");
|
|
||||||
|
|
||||||
// Simulate the mousemove event
|
|
||||||
simulateMouseEvent(event, "mousemove");
|
|
||||||
|
|
||||||
// Simulate the mousedown event
|
|
||||||
simulateMouseEvent(event, "mousedown");
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the jQuery UI widget's touchmove events
|
|
||||||
* @param {Object} event The document's touchmove event
|
|
||||||
*/
|
|
||||||
mouseProto._touchMove = function (event) {
|
|
||||||
// Ignore event if not handled
|
|
||||||
if (!touchHandled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interaction was not a click
|
|
||||||
this._touchMoved = true;
|
|
||||||
|
|
||||||
// Simulate the mousemove event
|
|
||||||
simulateMouseEvent(event, "mousemove");
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the jQuery UI widget's touchend events
|
|
||||||
* @param {Object} event The document's touchend event
|
|
||||||
*/
|
|
||||||
mouseProto._touchEnd = function (event) {
|
|
||||||
// Ignore event if not handled
|
|
||||||
if (!touchHandled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate the mouseup event
|
|
||||||
simulateMouseEvent(event, "mouseup");
|
|
||||||
|
|
||||||
// Simulate the mouseout event
|
|
||||||
simulateMouseEvent(event, "mouseout");
|
|
||||||
|
|
||||||
// If the touch interaction did not move, it should trigger a click
|
|
||||||
if (!this._touchMoved) {
|
|
||||||
// Simulate the click event
|
|
||||||
simulateMouseEvent(event, "click");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unset the flag to allow other widgets to inherit the touch event
|
|
||||||
touchHandled = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A duck punch of the $.ui.mouse _mouseInit method to support touch events.
|
|
||||||
* This method extends the widget with bound touch event handlers that
|
|
||||||
* translate touch events to mouse events and pass them to the widget's
|
|
||||||
* original mouse event handling methods.
|
|
||||||
*/
|
|
||||||
mouseProto._mouseInit = function () {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Delegate the touch handlers to the widget's element
|
|
||||||
self.element.bind({
|
|
||||||
touchstart: $.proxy(self, "_touchStart"),
|
|
||||||
touchmove: $.proxy(self, "_touchMove"),
|
|
||||||
touchend: $.proxy(self, "_touchEnd"),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Call the original $.ui.mouse init method
|
|
||||||
_mouseInit.call(self);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the touch event handlers
|
|
||||||
*/
|
|
||||||
mouseProto._mouseDestroy = function () {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Delegate the touch handlers to the widget's element
|
|
||||||
self.element.unbind({
|
|
||||||
touchstart: $.proxy(self, "_touchStart"),
|
|
||||||
touchmove: $.proxy(self, "_touchMove"),
|
|
||||||
touchend: $.proxy(self, "_touchEnd"),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Call the original $.ui.mouse destroy method
|
|
||||||
_mouseDestroy.call(self);
|
|
||||||
};
|
|
||||||
})(jQuery);
|
|
@ -1,340 +0,0 @@
|
|||||||
!(function () {
|
|
||||||
"use strict";
|
|
||||||
const e = document.getElementsByClassName("accordion-header");
|
|
||||||
|
|
||||||
function t(e, t) {
|
|
||||||
e.classList.add("is-active"), (t.style.maxHeight = t.scrollHeight + "px");
|
|
||||||
}
|
|
||||||
|
|
||||||
function n(e, t) {
|
|
||||||
e.classList.remove("is-active"), (t.style.maxHeight = null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.length > 0)
|
|
||||||
for (let i = 0; i < e.length; i++) {
|
|
||||||
const s = e[i],
|
|
||||||
a = s.parentNode,
|
|
||||||
l = s.nextElementSibling;
|
|
||||||
a.classList.contains("is-active") && t(a, l),
|
|
||||||
s.addEventListener("click", function () {
|
|
||||||
a.classList.contains("is-active") ? n(a, l) : t(a, l);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})(),
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
let e = {
|
|
||||||
touchStartX: 0,
|
|
||||||
touchEndX: 0,
|
|
||||||
minSwipePixels: 30,
|
|
||||||
detectionZone: void 0,
|
|
||||||
swipeCallback: function () {},
|
|
||||||
init: function (t, n) {
|
|
||||||
(e.swipeCallback = n),
|
|
||||||
t.addEventListener(
|
|
||||||
"touchstart",
|
|
||||||
function (t) {
|
|
||||||
e.touchStartX = t.changedTouches[0].screenX;
|
|
||||||
},
|
|
||||||
!1
|
|
||||||
),
|
|
||||||
t.addEventListener(
|
|
||||||
"touchend",
|
|
||||||
function (t) {
|
|
||||||
(e.touchEndX = t.changedTouches[0].screenX),
|
|
||||||
e.handleSwipeGesture();
|
|
||||||
},
|
|
||||||
!1
|
|
||||||
);
|
|
||||||
},
|
|
||||||
handleSwipeGesture: function () {
|
|
||||||
let t, n;
|
|
||||||
e.touchEndX <= e.touchStartX &&
|
|
||||||
((n = e.touchStartX - e.touchEndX), (t = "left")),
|
|
||||||
e.touchEndX >= e.touchStartX &&
|
|
||||||
((n = e.touchEndX - e.touchStartX), (t = "right")),
|
|
||||||
n > e.minSwipePixels && "undefined" !== t && e.swipe(t, n);
|
|
||||||
},
|
|
||||||
swipe: function (t, n) {
|
|
||||||
let i = {};
|
|
||||||
(i.direction = t), (i.movedPixels = n), e.swipeCallback(i);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const t = document.getElementsByClassName("carousel-items");
|
|
||||||
|
|
||||||
function n(e, t) {
|
|
||||||
void 0 === t && (t = "next");
|
|
||||||
let n = e.getElementsByClassName("carousel-item is-active")[0],
|
|
||||||
i = "next" === t ? n.nextElementSibling : n.previousElementSibling,
|
|
||||||
s = n.getAttribute("data-carousel"),
|
|
||||||
a = e.parentNode.getElementsByClassName("carousel-bullet")[s],
|
|
||||||
l = "next" === t ? a.nextElementSibling : a.previousElementSibling;
|
|
||||||
n.classList.remove("is-active"),
|
|
||||||
a.classList.remove("is-active"),
|
|
||||||
i
|
|
||||||
? (i.classList.add("is-active"), l.classList.add("is-active"))
|
|
||||||
: "next" === t
|
|
||||||
? (e.firstElementChild.classList.add("is-active"),
|
|
||||||
e.parentNode
|
|
||||||
.getElementsByClassName("carousel-bullets")[0]
|
|
||||||
.firstElementChild.classList.add("is-active"))
|
|
||||||
: (e.lastElementChild.classList.add("is-active"),
|
|
||||||
e.parentNode
|
|
||||||
.getElementsByClassName("carousel-bullets")[0]
|
|
||||||
.lastElementChild.classList.add("is-active"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function i(e, t) {
|
|
||||||
let n,
|
|
||||||
i = 0;
|
|
||||||
for (let e = 0; e < t.length; e++)
|
|
||||||
(t[0].parentNode.style.minHeight = i + "px"),
|
|
||||||
t[e].classList.add("is-loading"),
|
|
||||||
(n = t[e].offsetHeight),
|
|
||||||
t[e].classList.remove("is-loading"),
|
|
||||||
n > i && (i = n);
|
|
||||||
t[0].parentNode.style.minHeight = i + "px";
|
|
||||||
}
|
|
||||||
|
|
||||||
function s(e) {
|
|
||||||
e && clearInterval(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t.length > 0)
|
|
||||||
for (let a = 0; a < t.length; a++) {
|
|
||||||
let l = t[a],
|
|
||||||
c = l.getElementsByClassName("carousel-item"),
|
|
||||||
o = 0,
|
|
||||||
r = l.getAttribute("data-autorotate");
|
|
||||||
const d = document.createElement("div");
|
|
||||||
(d.className = "carousel-bullets"),
|
|
||||||
l.parentNode.insertBefore(d, l.nextSibling);
|
|
||||||
for (let e = 0; e < c.length; e++) {
|
|
||||||
c[e].setAttribute("data-carousel", e),
|
|
||||||
c[e].classList.contains("is-active") && (o = e);
|
|
||||||
let t = document.createElement("button");
|
|
||||||
(t.className = "carousel-bullet"),
|
|
||||||
t.setAttribute("data-bullet", e),
|
|
||||||
l.parentNode
|
|
||||||
.getElementsByClassName("carousel-bullets")[0]
|
|
||||||
.appendChild(t);
|
|
||||||
}
|
|
||||||
c[o].classList.add("is-active");
|
|
||||||
let u = l.parentNode.getElementsByClassName("carousel-bullet");
|
|
||||||
u[o].classList.add("is-active"),
|
|
||||||
i(0, c),
|
|
||||||
window.addEventListener("resize", function () {
|
|
||||||
i(0, c);
|
|
||||||
});
|
|
||||||
let m = !1;
|
|
||||||
r &&
|
|
||||||
(m = setInterval(function () {
|
|
||||||
n(l, "next");
|
|
||||||
}, r));
|
|
||||||
for (let e = 0; e < u.length; e++) {
|
|
||||||
let t = u[e];
|
|
||||||
t.addEventListener("click", function (e) {
|
|
||||||
if ((e.preventDefault(), t.classList.contains("is-active"))) return;
|
|
||||||
for (let e = 0; e < u.length; e++)
|
|
||||||
u[e].classList.remove("is-active");
|
|
||||||
for (let e = 0; e < c.length; e++)
|
|
||||||
c[e].classList.remove("is-active");
|
|
||||||
let n = this.getAttribute("data-bullet");
|
|
||||||
c[n].classList.add("is-active"),
|
|
||||||
this.classList.add("is-active"),
|
|
||||||
s(m);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
e.init(l, function (e) {
|
|
||||||
"left" === e.direction
|
|
||||||
? n(l, "next")
|
|
||||||
: "right" === e.direction && n(l, "prev"),
|
|
||||||
s(m);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})(),
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
document.documentElement.classList.remove("no-js"),
|
|
||||||
document.documentElement.classList.add("js"),
|
|
||||||
window.addEventListener("load", function () {
|
|
||||||
document.body.classList.add("is-loaded");
|
|
||||||
});
|
|
||||||
})(),
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const e = document.getElementById("header-nav-toggle"),
|
|
||||||
t = document.getElementById("header-nav");
|
|
||||||
e &&
|
|
||||||
(e.addEventListener("click", function () {
|
|
||||||
document.body.classList.toggle("off-nav-is-active"),
|
|
||||||
t.classList.toggle("is-active"),
|
|
||||||
t.style.maxHeight
|
|
||||||
? (t.style.maxHeight = null)
|
|
||||||
: (t.style.maxHeight = t.scrollHeight + "px"),
|
|
||||||
"true" === this.getAttribute("aria-expanded")
|
|
||||||
? this.setAttribute("aria-expanded", "false")
|
|
||||||
: this.setAttribute("aria-expanded", "true");
|
|
||||||
}),
|
|
||||||
document.addEventListener("click", function (n) {
|
|
||||||
n.target === t ||
|
|
||||||
n.target === e ||
|
|
||||||
t.contains(n.target) ||
|
|
||||||
(document.body.classList.remove("off-nav-is-active"),
|
|
||||||
t.classList.remove("is-active"),
|
|
||||||
(t.style.maxHeight = null),
|
|
||||||
e.setAttribute("aria-expanded", "false"));
|
|
||||||
}));
|
|
||||||
})(),
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const e = document.getElementsByClassName("modal"),
|
|
||||||
t = document.getElementsByClassName("modal-trigger");
|
|
||||||
|
|
||||||
function n() {
|
|
||||||
document.body.classList.remove("modal-is-active");
|
|
||||||
for (let t = 0; t < e.length; t++) e[t].classList.remove("is-active");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.length > 0 && t.length > 0)
|
|
||||||
for (let e = 0; e < t.length; e++) {
|
|
||||||
let n = t[e],
|
|
||||||
i = document.getElementById(n.getAttribute("aria-controls"));
|
|
||||||
i &&
|
|
||||||
(n.hasAttribute("data-video") &&
|
|
||||||
(null !== i.querySelector("iframe")
|
|
||||||
? i
|
|
||||||
.querySelector("iframe")
|
|
||||||
.setAttribute("src", n.getAttribute("data-video"))
|
|
||||||
: null !== i.querySelector("video") &&
|
|
||||||
i
|
|
||||||
.querySelector("video")
|
|
||||||
.setAttribute("src", n.getAttribute("data-video"))),
|
|
||||||
n.addEventListener("click", function (e) {
|
|
||||||
var t;
|
|
||||||
e.preventDefault(),
|
|
||||||
n.hasAttribute("aria-controls") &&
|
|
||||||
(t = i) &&
|
|
||||||
(document.body.classList.add("modal-is-active"),
|
|
||||||
t.classList.add("is-active"));
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
document.addEventListener("click", function (e) {
|
|
||||||
(e.target.classList.contains("modal") ||
|
|
||||||
e.target.classList.contains("modal-close-trigger")) &&
|
|
||||||
(e.preventDefault(), n());
|
|
||||||
}),
|
|
||||||
document.addEventListener("keydown", function (e) {
|
|
||||||
27 === (e || window.event).keyCode && n();
|
|
||||||
});
|
|
||||||
})(),
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const e = document.getElementById("pricing-toggle");
|
|
||||||
|
|
||||||
function t() {
|
|
||||||
const t = document.getElementsByClassName("pricing-switchable");
|
|
||||||
if (e.checked)
|
|
||||||
for (let e = 0; e < t.length; e++)
|
|
||||||
t[e].innerHTML = t[e].getAttribute("data-pricing-yearly");
|
|
||||||
else
|
|
||||||
for (let e = 0; e < t.length; e++)
|
|
||||||
t[e].innerHTML = t[e].getAttribute("data-pricing-monthly");
|
|
||||||
}
|
|
||||||
|
|
||||||
e && (window.addEventListener("load", t), e.addEventListener("change", t));
|
|
||||||
})(),
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const e = document.querySelectorAll("[class*=reveal-]");
|
|
||||||
let t = window.innerHeight;
|
|
||||||
|
|
||||||
function n(e, t) {
|
|
||||||
var n = 0;
|
|
||||||
return function () {
|
|
||||||
var i = new Date().getTime();
|
|
||||||
if (!(i - n < e)) return (n = i), t.apply(void 0, arguments);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function i() {
|
|
||||||
for (let i = 0; i < e.length; i++) {
|
|
||||||
let s = e[i],
|
|
||||||
a = s.getAttribute("data-reveal-delay"),
|
|
||||||
l = s.getAttribute("data-reveal-offset")
|
|
||||||
? s.getAttribute("data-reveal-offset")
|
|
||||||
: "200",
|
|
||||||
c = s.getAttribute("data-reveal-container")
|
|
||||||
? s.closest(s.getAttribute("data-reveal-container"))
|
|
||||||
: s;
|
|
||||||
(n = l),
|
|
||||||
c.getBoundingClientRect().top <= t - n &&
|
|
||||||
!s.classList.contains("is-revealed") &&
|
|
||||||
(a && 0 !== a
|
|
||||||
? setTimeout(function () {
|
|
||||||
s.classList.add("is-revealed");
|
|
||||||
}, a)
|
|
||||||
: s.classList.add("is-revealed"));
|
|
||||||
}
|
|
||||||
var n;
|
|
||||||
!(function () {
|
|
||||||
if (
|
|
||||||
e.length >
|
|
||||||
document.querySelectorAll("[class*=reveal-].is-revealed").length
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
window.removeEventListener("load", i),
|
|
||||||
window.removeEventListener("scroll", s),
|
|
||||||
window.removeEventListener("resize", a);
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
function s() {
|
|
||||||
n(30, i());
|
|
||||||
}
|
|
||||||
|
|
||||||
function a() {
|
|
||||||
(t = window.innerHeight), n(30, i());
|
|
||||||
}
|
|
||||||
|
|
||||||
e.length > 0 &&
|
|
||||||
document.body.classList.contains("has-animations") &&
|
|
||||||
(window.addEventListener("load", i),
|
|
||||||
window.addEventListener("scroll", s),
|
|
||||||
window.addEventListener("resize", a));
|
|
||||||
})(),
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const e = document.getElementsByClassName("smooth-scroll"),
|
|
||||||
t = (e, n, i, s, a) => {
|
|
||||||
const l = n - e;
|
|
||||||
let c = l / i;
|
|
||||||
const o = (function (e) {
|
|
||||||
return e < 0.5 ? 2 * e * e : (4 - 2 * e) * e - 1;
|
|
||||||
})((c = Math.min(c, 1)));
|
|
||||||
window.scroll(0, a + s * o),
|
|
||||||
l < i &&
|
|
||||||
window.requestAnimationFrame((n) => {
|
|
||||||
const l = n || new Date().getTime();
|
|
||||||
t(e, l, i, s, a);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (e.length > 0)
|
|
||||||
for (let n = 0; n < e.length; n++) {
|
|
||||||
e[n].addEventListener("click", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const n = e.target.closest(".smooth-scroll"),
|
|
||||||
i = n.href.split("#")[1],
|
|
||||||
s = document.getElementById(i),
|
|
||||||
a = n.getAttribute("data-duration") || 1e3;
|
|
||||||
s &&
|
|
||||||
window.requestAnimationFrame((e) => {
|
|
||||||
const n = e || new Date().getTime(),
|
|
||||||
i = n,
|
|
||||||
l = window.pageYOffset,
|
|
||||||
c = s.getBoundingClientRect().top;
|
|
||||||
t(i, n, a, c, l);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,34 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const navToggle = document.getElementById("header-nav-toggle");
|
|
||||||
const mainNav = document.getElementById("header-nav");
|
|
||||||
|
|
||||||
if (navToggle) {
|
|
||||||
// Open menu
|
|
||||||
navToggle.addEventListener("click", function () {
|
|
||||||
document.body.classList.toggle("off-nav-is-active");
|
|
||||||
mainNav.classList.toggle("is-active");
|
|
||||||
if (mainNav.style.maxHeight) {
|
|
||||||
mainNav.style.maxHeight = null;
|
|
||||||
} else {
|
|
||||||
mainNav.style.maxHeight = mainNav.scrollHeight + "px";
|
|
||||||
}
|
|
||||||
this.getAttribute("aria-expanded") === "true"
|
|
||||||
? this.setAttribute("aria-expanded", "false")
|
|
||||||
: this.setAttribute("aria-expanded", "true");
|
|
||||||
});
|
|
||||||
// Close menu
|
|
||||||
document.addEventListener("click", function (e) {
|
|
||||||
if (
|
|
||||||
e.target !== mainNav &&
|
|
||||||
e.target !== navToggle &&
|
|
||||||
!mainNav.contains(e.target)
|
|
||||||
) {
|
|
||||||
document.body.classList.remove("off-nav-is-active");
|
|
||||||
mainNav.classList.remove("is-active");
|
|
||||||
mainNav.style.maxHeight = null;
|
|
||||||
navToggle.setAttribute("aria-expanded", "false");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,65 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const modals = document.getElementsByClassName("modal");
|
|
||||||
const modalTriggers = document.getElementsByClassName("modal-trigger");
|
|
||||||
|
|
||||||
function openModal(el) {
|
|
||||||
if (el) {
|
|
||||||
document.body.classList.add("modal-is-active");
|
|
||||||
el.classList.add("is-active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeModals() {
|
|
||||||
document.body.classList.remove("modal-is-active");
|
|
||||||
for (let i = 0; i < modals.length; i++) {
|
|
||||||
modals[i].classList.remove("is-active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modals.length > 0 && modalTriggers.length > 0) {
|
|
||||||
for (let i = 0; i < modalTriggers.length; i++) {
|
|
||||||
let modalTrigger = modalTriggers[i];
|
|
||||||
let modal = document.getElementById(
|
|
||||||
modalTrigger.getAttribute("aria-controls")
|
|
||||||
);
|
|
||||||
if (modal) {
|
|
||||||
// Modal video
|
|
||||||
if (modalTrigger.hasAttribute("data-video")) {
|
|
||||||
if (modal.querySelector("iframe") !== null) {
|
|
||||||
modal
|
|
||||||
.querySelector("iframe")
|
|
||||||
.setAttribute("src", modalTrigger.getAttribute("data-video"));
|
|
||||||
} else if (modal.querySelector("video") !== null) {
|
|
||||||
modal
|
|
||||||
.querySelector("video")
|
|
||||||
.setAttribute("src", modalTrigger.getAttribute("data-video"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modalTrigger.addEventListener("click", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (modalTrigger.hasAttribute("aria-controls")) {
|
|
||||||
openModal(modal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("click", function (e) {
|
|
||||||
if (
|
|
||||||
e.target.classList.contains("modal") ||
|
|
||||||
e.target.classList.contains("modal-close-trigger")
|
|
||||||
) {
|
|
||||||
e.preventDefault();
|
|
||||||
closeModals();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("keydown", function (event) {
|
|
||||||
var e = event || window.event;
|
|
||||||
if (e.keyCode === 27) {
|
|
||||||
closeModals();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
@ -1,124 +0,0 @@
|
|||||||
var adjectives = [
|
|
||||||
"small",
|
|
||||||
"big",
|
|
||||||
"large",
|
|
||||||
"smelly",
|
|
||||||
"new",
|
|
||||||
"happy",
|
|
||||||
"shiny",
|
|
||||||
"old",
|
|
||||||
"clean",
|
|
||||||
"nice",
|
|
||||||
"bad",
|
|
||||||
"cool",
|
|
||||||
"hot",
|
|
||||||
"cold",
|
|
||||||
"warm",
|
|
||||||
"hungry",
|
|
||||||
"slow",
|
|
||||||
"fast",
|
|
||||||
"red",
|
|
||||||
"white",
|
|
||||||
"black",
|
|
||||||
"blue",
|
|
||||||
"green",
|
|
||||||
"basic",
|
|
||||||
"strong",
|
|
||||||
"cute",
|
|
||||||
"poor",
|
|
||||||
"nice",
|
|
||||||
"huge",
|
|
||||||
"rare",
|
|
||||||
"lucky",
|
|
||||||
"weak",
|
|
||||||
"tall",
|
|
||||||
"short",
|
|
||||||
"tiny",
|
|
||||||
"great",
|
|
||||||
"long",
|
|
||||||
"single",
|
|
||||||
"rich",
|
|
||||||
"young",
|
|
||||||
"dirty",
|
|
||||||
"fresh",
|
|
||||||
"brown",
|
|
||||||
"dark",
|
|
||||||
"crazy",
|
|
||||||
"sad",
|
|
||||||
"loud",
|
|
||||||
"brave",
|
|
||||||
"calm",
|
|
||||||
"silly",
|
|
||||||
"smart",
|
|
||||||
];
|
|
||||||
|
|
||||||
var nouns = [
|
|
||||||
"dog",
|
|
||||||
"bat",
|
|
||||||
"wrench",
|
|
||||||
"apple",
|
|
||||||
"pear",
|
|
||||||
"ghost",
|
|
||||||
"cat",
|
|
||||||
"wolf",
|
|
||||||
"squid",
|
|
||||||
"goat",
|
|
||||||
"snail",
|
|
||||||
"hat",
|
|
||||||
"sock",
|
|
||||||
"plum",
|
|
||||||
"bear",
|
|
||||||
"snake",
|
|
||||||
"turtle",
|
|
||||||
"horse",
|
|
||||||
"spoon",
|
|
||||||
"fork",
|
|
||||||
"spider",
|
|
||||||
"tree",
|
|
||||||
"chair",
|
|
||||||
"table",
|
|
||||||
"couch",
|
|
||||||
"towel",
|
|
||||||
"panda",
|
|
||||||
"bread",
|
|
||||||
"grape",
|
|
||||||
"cake",
|
|
||||||
"brick",
|
|
||||||
"rat",
|
|
||||||
"mouse",
|
|
||||||
"bird",
|
|
||||||
"oven",
|
|
||||||
"phone",
|
|
||||||
"photo",
|
|
||||||
"frog",
|
|
||||||
"bear",
|
|
||||||
"camel",
|
|
||||||
"sheep",
|
|
||||||
"shark",
|
|
||||||
"tiger",
|
|
||||||
"zebra",
|
|
||||||
"duck",
|
|
||||||
"eagle",
|
|
||||||
"fish",
|
|
||||||
"kitten",
|
|
||||||
"lobster",
|
|
||||||
"monkey",
|
|
||||||
"owl",
|
|
||||||
"puppy",
|
|
||||||
"pig",
|
|
||||||
"rabbit",
|
|
||||||
"fox",
|
|
||||||
"whale",
|
|
||||||
"beaver",
|
|
||||||
"gorilla",
|
|
||||||
"lizard",
|
|
||||||
"parrot",
|
|
||||||
"sloth",
|
|
||||||
"swan",
|
|
||||||
];
|
|
||||||
|
|
||||||
var adjective = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
||||||
var noun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
||||||
noun = noun.charAt(0).toUpperCase() + noun.substring(1);
|
|
||||||
adjective = adjective.charAt(0).toUpperCase() + adjective.substring(1);
|
|
||||||
document.getElementById("input-01").value = adjective + noun;
|
|
@ -1,26 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const pricingToggle = document.getElementById("pricing-toggle");
|
|
||||||
|
|
||||||
if (pricingToggle) {
|
|
||||||
window.addEventListener("load", pricingSwitch);
|
|
||||||
pricingToggle.addEventListener("change", pricingSwitch);
|
|
||||||
}
|
|
||||||
|
|
||||||
function pricingSwitch() {
|
|
||||||
const switchables = document.getElementsByClassName("pricing-switchable");
|
|
||||||
if (pricingToggle.checked) {
|
|
||||||
for (let i = 0; i < switchables.length; i++) {
|
|
||||||
switchables[i].innerHTML = switchables[i].getAttribute(
|
|
||||||
"data-pricing-yearly"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < switchables.length; i++) {
|
|
||||||
switchables[i].innerHTML = switchables[i].getAttribute(
|
|
||||||
"data-pricing-monthly"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,76 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const revealEl = document.querySelectorAll("[class*=reveal-]");
|
|
||||||
let viewportHeight = window.innerHeight;
|
|
||||||
|
|
||||||
function throttle(delay, fn) {
|
|
||||||
var lastCall = 0;
|
|
||||||
return function () {
|
|
||||||
var now = new Date().getTime();
|
|
||||||
if (now - lastCall < delay) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastCall = now;
|
|
||||||
return fn.apply(void 0, arguments);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function elementIsVisible(el, offset) {
|
|
||||||
return el.getBoundingClientRect().top <= viewportHeight - offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
function revealElements() {
|
|
||||||
for (let i = 0; i < revealEl.length; i++) {
|
|
||||||
let el = revealEl[i];
|
|
||||||
let revealDelay = el.getAttribute("data-reveal-delay");
|
|
||||||
let revealOffset = el.getAttribute("data-reveal-offset")
|
|
||||||
? el.getAttribute("data-reveal-offset")
|
|
||||||
: "200";
|
|
||||||
let listenedEl = el.getAttribute("data-reveal-container")
|
|
||||||
? el.closest(el.getAttribute("data-reveal-container"))
|
|
||||||
: el;
|
|
||||||
if (
|
|
||||||
elementIsVisible(listenedEl, revealOffset) &&
|
|
||||||
!el.classList.contains("is-revealed")
|
|
||||||
) {
|
|
||||||
if (revealDelay && revealDelay !== 0) {
|
|
||||||
setTimeout(function () {
|
|
||||||
el.classList.add("is-revealed");
|
|
||||||
}, revealDelay);
|
|
||||||
} else {
|
|
||||||
el.classList.add("is-revealed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
revealDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
function revealScroll() {
|
|
||||||
throttle(30, revealElements());
|
|
||||||
}
|
|
||||||
|
|
||||||
function revealResize() {
|
|
||||||
viewportHeight = window.innerHeight;
|
|
||||||
throttle(30, revealElements());
|
|
||||||
}
|
|
||||||
|
|
||||||
function revealDone() {
|
|
||||||
if (
|
|
||||||
revealEl.length >
|
|
||||||
document.querySelectorAll("[class*=reveal-].is-revealed").length
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
window.removeEventListener("load", revealElements);
|
|
||||||
window.removeEventListener("scroll", revealScroll);
|
|
||||||
window.removeEventListener("resize", revealResize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
revealEl.length > 0 &&
|
|
||||||
document.body.classList.contains("has-animations")
|
|
||||||
) {
|
|
||||||
window.addEventListener("load", revealElements);
|
|
||||||
window.addEventListener("scroll", revealScroll);
|
|
||||||
window.addEventListener("resize", revealResize);
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,69 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
const smoothScrollLinks = document.getElementsByClassName("smooth-scroll");
|
|
||||||
|
|
||||||
const easeInOutQuad = function (t) {
|
|
||||||
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollToEl = (
|
|
||||||
startTime,
|
|
||||||
currentTime,
|
|
||||||
duration,
|
|
||||||
scrollEndElemTop,
|
|
||||||
startScrollOffset
|
|
||||||
) => {
|
|
||||||
const runtime = currentTime - startTime;
|
|
||||||
let progress = runtime / duration;
|
|
||||||
|
|
||||||
progress = Math.min(progress, 1);
|
|
||||||
|
|
||||||
const ease = easeInOutQuad(progress);
|
|
||||||
|
|
||||||
window.scroll(0, startScrollOffset + scrollEndElemTop * ease);
|
|
||||||
if (runtime < duration) {
|
|
||||||
window.requestAnimationFrame((timestamp) => {
|
|
||||||
const currentTime = timestamp || new Date().getTime();
|
|
||||||
scrollToEl(
|
|
||||||
startTime,
|
|
||||||
currentTime,
|
|
||||||
duration,
|
|
||||||
scrollEndElemTop,
|
|
||||||
startScrollOffset
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (smoothScrollLinks.length > 0) {
|
|
||||||
for (let i = 0; i < smoothScrollLinks.length; i++) {
|
|
||||||
const smoothScrollLink = smoothScrollLinks[i];
|
|
||||||
|
|
||||||
smoothScrollLink.addEventListener("click", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const link = e.target.closest(".smooth-scroll");
|
|
||||||
const targetId = link.href.split("#")[1];
|
|
||||||
const target = document.getElementById(targetId);
|
|
||||||
const duration = link.getAttribute("data-duration") || 1000;
|
|
||||||
|
|
||||||
if (!target) return;
|
|
||||||
|
|
||||||
window.requestAnimationFrame((timestamp) => {
|
|
||||||
const stamp = timestamp || new Date().getTime();
|
|
||||||
const start = stamp;
|
|
||||||
|
|
||||||
const startScrollOffset = window.pageYOffset;
|
|
||||||
const scrollEndElemTop = target.getBoundingClientRect().top;
|
|
||||||
|
|
||||||
scrollToEl(
|
|
||||||
start,
|
|
||||||
stamp,
|
|
||||||
duration,
|
|
||||||
scrollEndElemTop,
|
|
||||||
startScrollOffset
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,192 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Snackbar v0.1.14
|
|
||||||
* http://polonel.com/Snackbar
|
|
||||||
*
|
|
||||||
* Copyright 2018 Chris Brame and other contributors
|
|
||||||
* Released under the MIT license
|
|
||||||
* https://github.com/polonel/Snackbar/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function (root, factory) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
if (typeof define === "function" && define.amd) {
|
|
||||||
define([], function () {
|
|
||||||
return (root.Snackbar = factory());
|
|
||||||
});
|
|
||||||
} else if (typeof module === "object" && module.exports) {
|
|
||||||
module.exports = root.Snackbar = factory();
|
|
||||||
} else {
|
|
||||||
root.Snackbar = factory();
|
|
||||||
}
|
|
||||||
})(this, function () {
|
|
||||||
var Snackbar = {};
|
|
||||||
|
|
||||||
Snackbar.current = null;
|
|
||||||
var $defaults = {
|
|
||||||
text: "Default Text",
|
|
||||||
textColor: "#FFFFFF",
|
|
||||||
width: "auto",
|
|
||||||
showAction: true,
|
|
||||||
actionText: "Dismiss",
|
|
||||||
actionTextAria: "Dismiss, Description for Screen Readers",
|
|
||||||
alertScreenReader: false,
|
|
||||||
actionTextColor: "#4CAF50",
|
|
||||||
showSecondButton: false,
|
|
||||||
secondButtonText: "",
|
|
||||||
secondButtonAria: "Description for Screen Readers",
|
|
||||||
secondButtonTextColor: "#4CAF50",
|
|
||||||
// backgroundColor: "#323232",
|
|
||||||
pos: "bottom-left",
|
|
||||||
duration: 5000,
|
|
||||||
customClass: "",
|
|
||||||
onActionClick: function (element) {
|
|
||||||
element.style.opacity = 0;
|
|
||||||
},
|
|
||||||
onSecondButtonClick: function (element) {},
|
|
||||||
onClose: function (element) {},
|
|
||||||
};
|
|
||||||
|
|
||||||
Snackbar.show = function ($options) {
|
|
||||||
var options = Extend(true, $defaults, $options);
|
|
||||||
|
|
||||||
if (Snackbar.current) {
|
|
||||||
Snackbar.current.style.opacity = 0;
|
|
||||||
setTimeout(
|
|
||||||
function () {
|
|
||||||
var $parent = this.parentElement;
|
|
||||||
if ($parent)
|
|
||||||
// possible null if too many/fast Snackbars
|
|
||||||
$parent.removeChild(this);
|
|
||||||
}.bind(Snackbar.current),
|
|
||||||
500
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Snackbar.snackbar = document.createElement("div");
|
|
||||||
Snackbar.snackbar.className = "snackbar-container " + options.customClass;
|
|
||||||
Snackbar.snackbar.style.width = options.width;
|
|
||||||
var $p = document.createElement("p");
|
|
||||||
$p.style.margin = 0;
|
|
||||||
$p.style.padding = 0;
|
|
||||||
$p.style.color = options.textColor;
|
|
||||||
$p.style.fontWeight = 300;
|
|
||||||
// $p.style.fontSize = "14px";
|
|
||||||
// $p.style.lineHeight = "1em";
|
|
||||||
$p.innerHTML = options.text;
|
|
||||||
Snackbar.snackbar.appendChild($p);
|
|
||||||
// Snackbar.snackbar.style.background = options.backgroundColor;
|
|
||||||
|
|
||||||
if (options.showSecondButton) {
|
|
||||||
var secondButton = document.createElement("button");
|
|
||||||
secondButton.className = "action";
|
|
||||||
secondButton.innerHTML = options.secondButtonText;
|
|
||||||
secondButton.setAttribute("aria-label", options.secondButtonAria);
|
|
||||||
secondButton.style.color = options.secondButtonTextColor;
|
|
||||||
secondButton.addEventListener("click", function () {
|
|
||||||
options.onSecondButtonClick(Snackbar.snackbar);
|
|
||||||
});
|
|
||||||
Snackbar.snackbar.appendChild(secondButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.showAction) {
|
|
||||||
var actionButton = document.createElement("button");
|
|
||||||
actionButton.className = "action";
|
|
||||||
actionButton.innerHTML = options.actionText;
|
|
||||||
actionButton.setAttribute("aria-label", options.actionTextAria);
|
|
||||||
actionButton.style.color = options.actionTextColor;
|
|
||||||
actionButton.addEventListener("click", function () {
|
|
||||||
options.onActionClick(Snackbar.snackbar);
|
|
||||||
});
|
|
||||||
Snackbar.snackbar.appendChild(actionButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.duration) {
|
|
||||||
setTimeout(
|
|
||||||
function () {
|
|
||||||
if (Snackbar.current === this) {
|
|
||||||
Snackbar.current.style.opacity = 0;
|
|
||||||
// When natural remove event occurs let's move the snackbar to its origins
|
|
||||||
Snackbar.current.style.top = "-100px";
|
|
||||||
Snackbar.current.style.bottom = "-100px";
|
|
||||||
}
|
|
||||||
}.bind(Snackbar.snackbar),
|
|
||||||
options.duration
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.alertScreenReader) {
|
|
||||||
Snackbar.snackbar.setAttribute("role", "alert");
|
|
||||||
}
|
|
||||||
|
|
||||||
Snackbar.snackbar.addEventListener(
|
|
||||||
"transitionend",
|
|
||||||
function (event, elapsed) {
|
|
||||||
if (event.propertyName === "opacity" && this.style.opacity === "0") {
|
|
||||||
if (typeof options.onClose === "function") options.onClose(this);
|
|
||||||
|
|
||||||
this.parentElement.removeChild(this);
|
|
||||||
if (Snackbar.current === this) {
|
|
||||||
Snackbar.current = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.bind(Snackbar.snackbar)
|
|
||||||
);
|
|
||||||
|
|
||||||
Snackbar.current = Snackbar.snackbar;
|
|
||||||
|
|
||||||
document.body.appendChild(Snackbar.snackbar);
|
|
||||||
var $bottom = getComputedStyle(Snackbar.snackbar).bottom;
|
|
||||||
var $top = getComputedStyle(Snackbar.snackbar).top;
|
|
||||||
Snackbar.snackbar.style.opacity = 1;
|
|
||||||
Snackbar.snackbar.className =
|
|
||||||
"snackbar-container " +
|
|
||||||
options.customClass +
|
|
||||||
" snackbar-pos " +
|
|
||||||
options.pos;
|
|
||||||
};
|
|
||||||
|
|
||||||
Snackbar.close = function () {
|
|
||||||
if (Snackbar.current) {
|
|
||||||
Snackbar.current.style.opacity = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pure JS Extend
|
|
||||||
// http://gomakethings.com/vanilla-javascript-version-of-jquery-extend/
|
|
||||||
var Extend = function () {
|
|
||||||
var extended = {};
|
|
||||||
var deep = false;
|
|
||||||
var i = 0;
|
|
||||||
var length = arguments.length;
|
|
||||||
|
|
||||||
if (Object.prototype.toString.call(arguments[0]) === "[object Boolean]") {
|
|
||||||
deep = arguments[0];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var merge = function (obj) {
|
|
||||||
for (var prop in obj) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
|
|
||||||
if (
|
|
||||||
deep &&
|
|
||||||
Object.prototype.toString.call(obj[prop]) === "[object Object]"
|
|
||||||
) {
|
|
||||||
extended[prop] = Extend(true, extended[prop], obj[prop]);
|
|
||||||
} else {
|
|
||||||
extended[prop] = obj[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (; i < length; i++) {
|
|
||||||
var obj = arguments[i];
|
|
||||||
merge(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
return extended;
|
|
||||||
};
|
|
||||||
|
|
||||||
return Snackbar;
|
|
||||||
});
|
|
@ -1,393 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" class="no-js">
|
|
||||||
<head>
|
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
||||||
<script
|
|
||||||
async
|
|
||||||
src="https://www.googletagmanager.com/gtag/js?id=UA-162048272-1"
|
|
||||||
></script>
|
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
|
|
||||||
function gtag() {
|
|
||||||
dataLayer.push(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
gtag("js", new Date());
|
|
||||||
gtag("config", "UA-162048272-1");
|
|
||||||
</script>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
||||||
<title>Zipcall</title>
|
|
||||||
<link rel="stylesheet" href="css/landing.css" />
|
|
||||||
<link rel="shortcut icon" href="images/logo.svg" />
|
|
||||||
<meta property="og:title" content="Zipcall - Decentralized video calls" />
|
|
||||||
<meta
|
|
||||||
property="og:description"
|
|
||||||
content="Decentralized video
|
|
||||||
calling provides real-time HD quality and latency simply
|
|
||||||
not available with traditional technology."
|
|
||||||
/>
|
|
||||||
<meta property="og:image" content="https://zipcall.io/images/preview.png" />
|
|
||||||
<meta property="og:url" content="https://zipcall.io/" />
|
|
||||||
</head>
|
|
||||||
<body class="has-animations">
|
|
||||||
<div class="body-wrap">
|
|
||||||
<header class="site-header reveal-from-top">
|
|
||||||
<div class="container">
|
|
||||||
<div class="site-header-inner">
|
|
||||||
<div class="brand">
|
|
||||||
<h1 class="m-0">
|
|
||||||
<a href="/"
|
|
||||||
><img src="images/logo.svg" alt="Neon" width="32" height="32"
|
|
||||||
/></a>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main class="site-content">
|
|
||||||
<section class="hero section illustration-section-01">
|
|
||||||
<div class="container">
|
|
||||||
<div class="hero-inner section-inner">
|
|
||||||
<div class="split-wrap invert-mobile">
|
|
||||||
<div class="split-item">
|
|
||||||
<div
|
|
||||||
class="hero-content split-item-content center-content-mobile"
|
|
||||||
>
|
|
||||||
<h1
|
|
||||||
class="mt-0 mb-16 reveal-from-bottom"
|
|
||||||
data-reveal-delay="150"
|
|
||||||
>
|
|
||||||
Zipcall.io<br />Free browser based video calling for
|
|
||||||
everyone.
|
|
||||||
</h1>
|
|
||||||
<p
|
|
||||||
class="mt-0 mb-32 reveal-from-bottom"
|
|
||||||
data-reveal-delay="300"
|
|
||||||
>
|
|
||||||
Simple, Secure, and Fast. Peer to peer video calling
|
|
||||||
provides quality and latency simply not available with
|
|
||||||
traditional technology.
|
|
||||||
</p>
|
|
||||||
<div class="reveal-from-bottom" data-reveal-delay="450">
|
|
||||||
<a
|
|
||||||
class="button button-primary button-wide-mobile pulse"
|
|
||||||
style="
|
|
||||||
border: 0;
|
|
||||||
background: linear-gradient(
|
|
||||||
100deg,
|
|
||||||
#376df9 0,
|
|
||||||
#ff5fa0 75%,
|
|
||||||
#ffc55a 100%
|
|
||||||
);
|
|
||||||
"
|
|
||||||
href="/newcall"
|
|
||||||
>Try now</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
@media (min-width: 641px) {
|
|
||||||
.hero .split-wrap .split-item {
|
|
||||||
min-height: 492px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="features-tiles section center-content">
|
|
||||||
<div class="container">
|
|
||||||
<div class="features-tiles-inner section-inner has-top-divider">
|
|
||||||
<div class="section-header">
|
|
||||||
<div
|
|
||||||
class="container-xs reveal-from-bottom"
|
|
||||||
data-reveal-delay="650"
|
|
||||||
>
|
|
||||||
<h2 class="mt-0 mb-16">See the whole picture</h2>
|
|
||||||
<p class="m-0">
|
|
||||||
Zipcall is built radically different. We left behind slow
|
|
||||||
bulky servers, opting for decentralized peer to peer
|
|
||||||
calling. We engineered a platform with maximum video quality
|
|
||||||
and lowest latency.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tiles-wrap">
|
|
||||||
<div class="tiles-item reveal-from-right">
|
|
||||||
<div class="tiles-item-inner">
|
|
||||||
<div class="features-tiles-item-header">
|
|
||||||
<div class="features-tiles-item-image mb-16">
|
|
||||||
<img
|
|
||||||
src="images/feature-tile-icon-02.svg"
|
|
||||||
alt="Feature tile icon 02"
|
|
||||||
width="72"
|
|
||||||
height="72"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="features-tiles-item-content">
|
|
||||||
<h4 class="mt-0 mb-8">Best Video Quality</h4>
|
|
||||||
<p class="m-0 text-sm">
|
|
||||||
State of the art video compression combined with our
|
|
||||||
scaling optimization makes your calls crystal clear.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tiles-item reveal-from-left">
|
|
||||||
<div class="tiles-item-inner">
|
|
||||||
<div class="features-tiles-item-header">
|
|
||||||
<div class="features-tiles-item-image mb-16">
|
|
||||||
<img
|
|
||||||
src="images/feature-tile-icon-04.svg"
|
|
||||||
alt="Feature tile icon 04"
|
|
||||||
width="72"
|
|
||||||
height="72"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="features-tiles-item-content">
|
|
||||||
<h4 class="mt-0 mb-8">No Download Required</h4>
|
|
||||||
<p class="m-0 text-sm">
|
|
||||||
No downloads. No plugins. No nonsense. Just open Zipcall
|
|
||||||
in your browser and get back to what matters most.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tiles-item reveal-from-right">
|
|
||||||
<div class="tiles-item-inner">
|
|
||||||
<div class="features-tiles-item-header">
|
|
||||||
<div class="features-tiles-item-image mb-16">
|
|
||||||
<img
|
|
||||||
src="images/feature-tile-icon-01.svg"
|
|
||||||
alt="Feature tile icon 01"
|
|
||||||
width="72"
|
|
||||||
height="72"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="features-tiles-item-content">
|
|
||||||
<h4 class="mt-0 mb-8">Lowest Latency</h4>
|
|
||||||
<p class="m-0 text-sm">
|
|
||||||
Breakthrough peer to peer WebRTC technology means your
|
|
||||||
video goes directly to the other person without a
|
|
||||||
server. No middleman. No extra stops.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tiles-item reveal-from-left">
|
|
||||||
<div class="tiles-item-inner">
|
|
||||||
<div class="features-tiles-item-header">
|
|
||||||
<div class="features-tiles-item-image mb-16">
|
|
||||||
<img
|
|
||||||
src="images/feature-tile-icon-03.svg"
|
|
||||||
alt="Feature tile icon 03"
|
|
||||||
width="72"
|
|
||||||
height="72"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="features-tiles-item-content">
|
|
||||||
<h4 class="mt-0 mb-8">Total Privacy</h4>
|
|
||||||
<p class="m-0 text-sm">
|
|
||||||
Each chat is single use, data stays between you and your
|
|
||||||
caller. Zipcall is built privacy first.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tiles-item reveal-from-right">
|
|
||||||
<div class="tiles-item-inner">
|
|
||||||
<div class="features-tiles-item-header">
|
|
||||||
<div class="features-tiles-item-image mb-16">
|
|
||||||
<img
|
|
||||||
src="images/feature-tile-icon-05.svg"
|
|
||||||
alt="Feature tile icon 05"
|
|
||||||
width="72"
|
|
||||||
height="72"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="features-tiles-item-content">
|
|
||||||
<h4 class="mt-0 mb-8">No Server Needed</h4>
|
|
||||||
<p class="m-0 text-sm">
|
|
||||||
Calls are entirely between you and your caller,
|
|
||||||
decentralized from any server. Call data never leaves
|
|
||||||
the browser. Cool right?
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tiles-item reveal-from-left">
|
|
||||||
<div class="tiles-item-inner">
|
|
||||||
<div class="features-tiles-item-header">
|
|
||||||
<div class="features-tiles-item-image mb-16">
|
|
||||||
<img
|
|
||||||
src="images/feature-tile-icon-06.svg"
|
|
||||||
alt="Feature tile icon 06"
|
|
||||||
width="72"
|
|
||||||
height="72"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="features-tiles-item-content">
|
|
||||||
<h4 class="mt-0 mb-8">Maximum Security</h4>
|
|
||||||
<p class="m-0 text-sm">
|
|
||||||
End to end state of the art encryption means your calls
|
|
||||||
are exactly that. Your calls.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="team section center-content">
|
|
||||||
<div class="container">
|
|
||||||
<div class="team-inner section-inner has-top-divider">
|
|
||||||
<div class="section-header center-content reveal-from-bottom">
|
|
||||||
<div class="container-xs">
|
|
||||||
<h2 class="mt-0 mb-16">
|
|
||||||
Meet the team
|
|
||||||
</h2>
|
|
||||||
<!-- <p class="m-0">-->
|
|
||||||
<!-- One man show... for now-->
|
|
||||||
<!-- </p>-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tiles-wrap">
|
|
||||||
<div class="tiles-item reveal-from-bottom">
|
|
||||||
<div class="tiles-item-inner">
|
|
||||||
<div class="team-item-header">
|
|
||||||
<div class="team-item-image mb-24">
|
|
||||||
<img
|
|
||||||
src="images/portrait.png"
|
|
||||||
alt="Team member 01"
|
|
||||||
width="180"
|
|
||||||
height="180"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="team-item-content">
|
|
||||||
<a target="_blank" href="https://ianramzy.com">
|
|
||||||
<h5 class="team-item-name mt-0 mb-4">Ian Ramzy</h5>
|
|
||||||
</a>
|
|
||||||
<div
|
|
||||||
class="team-item-role text-xxs fw-500 tt-u text-color-primary mb-8"
|
|
||||||
>
|
|
||||||
Software Engineer
|
|
||||||
</div>
|
|
||||||
<p class="m-0 text-sm">
|
|
||||||
Connecting the world together one Zipcall at a time.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-inner has-top-divider">
|
|
||||||
<div class="container-xs">
|
|
||||||
<div class="section-header center-content">
|
|
||||||
<h2 class="m-0">
|
|
||||||
Try an easier, more secure way of calling.
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div class="center-content">
|
|
||||||
<a
|
|
||||||
class="button button-primary button-wide-mobile pulse"
|
|
||||||
style="
|
|
||||||
border: 0;
|
|
||||||
background: linear-gradient(
|
|
||||||
100deg,
|
|
||||||
#376df9 0,
|
|
||||||
#ff5fa0 75%,
|
|
||||||
#ffc55a 100%
|
|
||||||
);
|
|
||||||
"
|
|
||||||
href="/newcall"
|
|
||||||
>Try now</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
<footer class="site-footer center-content-mobile">
|
|
||||||
<div class="container">
|
|
||||||
<div class="site-footer-inner">
|
|
||||||
<div class="footer-top space-between text-xxs">
|
|
||||||
<div class="brand">
|
|
||||||
<a href="/"
|
|
||||||
><img src="images/logo.svg" alt="Neon" width="32" height="32"
|
|
||||||
/></a>
|
|
||||||
</div>
|
|
||||||
<div class="footer-social">
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/ianramzy/decentralized-video-chat"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<title>GitHub</title>
|
|
||||||
<path
|
|
||||||
d="M7.95 0C3.578 0 0 3.578 0 7.95c0 3.479 2.286 6.46 5.466 7.553.397.1.497-.199.497-.397v-1.392c-2.187.497-2.683-.994-2.683-.994-.398-.894-.895-1.192-.895-1.192-.696-.497.1-.497.1-.497.795.1 1.192.795 1.192.795.696 1.292 1.888.894 2.286.696.1-.497.298-.895.497-1.093-1.79-.2-3.578-.895-3.578-3.976 0-.894.298-1.59.795-2.087-.1-.198-.397-.993.1-2.086 0 0 .695-.2 2.186.795a6.408 6.408 0 0 1 1.987-.299c.696 0 1.392.1 1.988.299 1.49-.994 2.186-.795 2.186-.795.398 1.093.199 1.888.1 2.086.496.597.795 1.292.795 2.087 0 3.081-1.889 3.677-3.677 3.876.298.398.596.895.596 1.59v2.187c0 .198.1.496.596.397C13.714 14.41 16 11.43 16 7.95 15.9 3.578 12.323 0 7.95 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="footer-bottom space-between text-xxs invert-order-desktop"
|
|
||||||
>
|
|
||||||
<nav class="footer-nav">
|
|
||||||
<ul class="list-reset">
|
|
||||||
<li>
|
|
||||||
<a target="_blank" href="https://ianramzy.com"
|
|
||||||
>Made with ❤️ by Ian Ramzy</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<!-- <li><a href="#">Contact</a></li>-->
|
|
||||||
<!-- <li><a href="#">About us</a></li>-->
|
|
||||||
<!-- <li><a href="#">FAQ's</a></li>-->
|
|
||||||
<!-- <li><a href="#">Support</a></li>-->
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<div class="footer-copyright">
|
|
||||||
© 2020 Zipcall, all rights reserved
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<script src="js/landing.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,180 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" class="no-js">
|
|
||||||
<head>
|
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
||||||
<script
|
|
||||||
async
|
|
||||||
src="https://www.googletagmanager.com/gtag/js?id=UA-162048272-1"
|
|
||||||
></script>
|
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
|
|
||||||
function gtag() {
|
|
||||||
dataLayer.push(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
gtag("js", new Date());
|
|
||||||
gtag("config", "UA-162048272-1");
|
|
||||||
</script>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
||||||
<title>Zipcall</title>
|
|
||||||
<link rel="stylesheet" href="css/landing.css" />
|
|
||||||
<link rel="stylesheet" href="css/newcall.css" />
|
|
||||||
<link rel="shortcut icon" href="images/logo.svg" />
|
|
||||||
<meta property="og:title" content="Zipcall - Decentralized video calls" />
|
|
||||||
<meta
|
|
||||||
property="og:description"
|
|
||||||
content="Decentralized video
|
|
||||||
calling provides real-time HD quality and latency simply
|
|
||||||
not available with traditional technology."
|
|
||||||
/>
|
|
||||||
<meta property="og:image" content="https://zipcall.io/images/preview.png" />
|
|
||||||
<meta property="og:url" content="https://zipcall.io/" />
|
|
||||||
</head>
|
|
||||||
<body class="has-animations">
|
|
||||||
<div class="body-wrap">
|
|
||||||
<header class="site-header reveal-from-top">
|
|
||||||
<div class="container">
|
|
||||||
<div class="site-header-inner">
|
|
||||||
<div class="brand">
|
|
||||||
<h1 class="m-0">
|
|
||||||
<a href="/"
|
|
||||||
><img src="images/logo.svg" alt="Neon" width="32" height="32"
|
|
||||||
/></a>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main class="site-content">
|
|
||||||
<section class="hero section illustration-section-01">
|
|
||||||
<div class="container">
|
|
||||||
<div class="hero-inner section-inner">
|
|
||||||
<div class="split-wrap invert-mobile">
|
|
||||||
<div class="split-item">
|
|
||||||
<div
|
|
||||||
class="hero-content split-item-content center-content-mobile"
|
|
||||||
>
|
|
||||||
<h1
|
|
||||||
class="mt-0 mb-16 reveal-from-bottom"
|
|
||||||
data-reveal-delay="150"
|
|
||||||
>
|
|
||||||
Pick name. <br />
|
|
||||||
Share URL. <br />
|
|
||||||
Start chatting.
|
|
||||||
</h1>
|
|
||||||
<p
|
|
||||||
class="mt-0 mb-32 reveal-from-bottom"
|
|
||||||
data-reveal-delay="300"
|
|
||||||
>
|
|
||||||
Each chat has its own disposable URL. Just pick a call
|
|
||||||
name and share your custom link. It's really that easy.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
@media (min-width: 641px) {
|
|
||||||
.hero .split-wrap .split-item {
|
|
||||||
min-height: 492px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="cta section center-content-mobile reveal-from-bottom">
|
|
||||||
<div class="container">
|
|
||||||
<div class="cta-inner section-inner cta-split">
|
|
||||||
<div class="cta-slogan">
|
|
||||||
<h3 class="m-0">
|
|
||||||
Pick a call name.<br />
|
|
||||||
How about this one?
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div class="cta-action">
|
|
||||||
<div class="mb-24">
|
|
||||||
<label class="form-label screen-reader" for="input-01"
|
|
||||||
>This is a label</label
|
|
||||||
>
|
|
||||||
<div class="form-group-desktop">
|
|
||||||
<input
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
id="input-01"
|
|
||||||
value="PurpleSquid"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
class="button button-primary pulse"
|
|
||||||
onclick="{window.location.href = '/join/' + document.getElementById('input-01').value}"
|
|
||||||
>
|
|
||||||
Go To My Call
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
<footer class="site-footer center-content-mobile">
|
|
||||||
<div class="container">
|
|
||||||
<div class="site-footer-inner">
|
|
||||||
<div class="footer-top space-between text-xxs">
|
|
||||||
<div class="brand">
|
|
||||||
<a href="/"
|
|
||||||
><img src="images/logo.svg" alt="Neon" width="32" height="32"
|
|
||||||
/></a>
|
|
||||||
</div>
|
|
||||||
<div class="footer-social">
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/ianramzy/decentralized-video-chat"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<title>GitHub</title>
|
|
||||||
<path
|
|
||||||
d="M7.95 0C3.578 0 0 3.578 0 7.95c0 3.479 2.286 6.46 5.466 7.553.397.1.497-.199.497-.397v-1.392c-2.187.497-2.683-.994-2.683-.994-.398-.894-.895-1.192-.895-1.192-.696-.497.1-.497.1-.497.795.1 1.192.795 1.192.795.696 1.292 1.888.894 2.286.696.1-.497.298-.895.497-1.093-1.79-.2-3.578-.895-3.578-3.976 0-.894.298-1.59.795-2.087-.1-.198-.397-.993.1-2.086 0 0 .695-.2 2.186.795a6.408 6.408 0 0 1 1.987-.299c.696 0 1.392.1 1.988.299 1.49-.994 2.186-.795 2.186-.795.398 1.093.199 1.888.1 2.086.496.597.795 1.292.795 2.087 0 3.081-1.889 3.677-3.677 3.876.298.398.596.895.596 1.59v2.187c0 .198.1.496.596.397C13.714 14.41 16 11.43 16 7.95 15.9 3.578 12.323 0 7.95 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="footer-bottom space-between text-xxs invert-order-desktop"
|
|
||||||
>
|
|
||||||
<nav class="footer-nav">
|
|
||||||
<ul class="list-reset">
|
|
||||||
<li>
|
|
||||||
<a target="_blank" href="https://ianramzy.com"
|
|
||||||
>Made with ❤️ by Ian Ramzy</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<!-- <li><a href="#">Contact</a></li>-->
|
|
||||||
<!-- <li><a href="#">About us</a></li>-->
|
|
||||||
<!-- <li><a href="#">FAQ's</a></li>-->
|
|
||||||
<!-- <li><a href="#">Support</a></li>-->
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<div class="footer-copyright">
|
|
||||||
© 2020 Zipcall, all rights reserved
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<script src="js/landing.js"></script>
|
|
||||||
<script src="js/newroom.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,136 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" class="no-js">
|
|
||||||
<head>
|
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
||||||
<script
|
|
||||||
async
|
|
||||||
src="https://www.googletagmanager.com/gtag/js?id=UA-162048272-1"
|
|
||||||
></script>
|
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
|
|
||||||
function gtag() {
|
|
||||||
dataLayer.push(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
gtag("js", new Date());
|
|
||||||
gtag("config", "UA-162048272-1");
|
|
||||||
</script>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
||||||
<title>Zipcall</title>
|
|
||||||
<link rel="stylesheet" href="css/landing.css" />
|
|
||||||
<link rel="shortcut icon" href="images/logo.svg" />
|
|
||||||
<meta property="og:title" content="Zipcall - Decentralized video calls" />
|
|
||||||
<meta
|
|
||||||
property="og:description"
|
|
||||||
content="Decentralized video
|
|
||||||
calling provides real-time HD quality and latency simply
|
|
||||||
not available with traditional technology."
|
|
||||||
/>
|
|
||||||
<meta property="og:image" content="https://zipcall.io/images/preview.png" />
|
|
||||||
<meta property="og:url" content="https://zipcall.io/" />
|
|
||||||
</head>
|
|
||||||
<body class="has-animations">
|
|
||||||
<div class="body-wrap">
|
|
||||||
<header class="site-header reveal-from-top">
|
|
||||||
<div class="container">
|
|
||||||
<div class="site-header-inner">
|
|
||||||
<div class="brand">
|
|
||||||
<h1 class="m-0">
|
|
||||||
<a href="/"
|
|
||||||
><img src="images/logo.svg" alt="Neon" width="32" height="32"
|
|
||||||
/></a>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main class="site-content">
|
|
||||||
<section class="hero section illustration-section-01">
|
|
||||||
<div class="container">
|
|
||||||
<div class="hero-inner section-inner">
|
|
||||||
<div class="split-wrap invert-mobile">
|
|
||||||
<div class="split-item">
|
|
||||||
<div
|
|
||||||
class="hero-content split-item-content center-content-mobile"
|
|
||||||
>
|
|
||||||
<h1
|
|
||||||
class="mt-0 mb-16 reveal-from-bottom"
|
|
||||||
data-reveal-delay="150"
|
|
||||||
>
|
|
||||||
Your browser is not supported. Try updating your browser
|
|
||||||
or try Chrome, Safari, or Firefox.
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
@media (min-width: 641px) {
|
|
||||||
.hero .split-wrap .split-item {
|
|
||||||
min-height: 492px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
<footer class="site-footer center-content-mobile">
|
|
||||||
<div class="container">
|
|
||||||
<div class="site-footer-inner">
|
|
||||||
<div class="footer-top space-between text-xxs">
|
|
||||||
<div class="brand">
|
|
||||||
<a href="/"
|
|
||||||
><img src="images/logo.svg" alt="Neon" width="32" height="32"
|
|
||||||
/></a>
|
|
||||||
</div>
|
|
||||||
<div class="footer-social">
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/ianramzy/decentralized-video-chat"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<title>GitHub</title>
|
|
||||||
<path
|
|
||||||
d="M7.95 0C3.578 0 0 3.578 0 7.95c0 3.479 2.286 6.46 5.466 7.553.397.1.497-.199.497-.397v-1.392c-2.187.497-2.683-.994-2.683-.994-.398-.894-.895-1.192-.895-1.192-.696-.497.1-.497.1-.497.795.1 1.192.795 1.192.795.696 1.292 1.888.894 2.286.696.1-.497.298-.895.497-1.093-1.79-.2-3.578-.895-3.578-3.976 0-.894.298-1.59.795-2.087-.1-.198-.397-.993.1-2.086 0 0 .695-.2 2.186.795a6.408 6.408 0 0 1 1.987-.299c.696 0 1.392.1 1.988.299 1.49-.994 2.186-.795 2.186-.795.398 1.093.199 1.888.1 2.086.496.597.795 1.292.795 2.087 0 3.081-1.889 3.677-3.677 3.876.298.398.596.895.596 1.59v2.187c0 .198.1.496.596.397C13.714 14.41 16 11.43 16 7.95 15.9 3.578 12.323 0 7.95 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="footer-bottom space-between text-xxs invert-order-desktop"
|
|
||||||
>
|
|
||||||
<nav class="footer-nav">
|
|
||||||
<ul class="list-reset">
|
|
||||||
<li>
|
|
||||||
<a target="_blank" href="https://ianramzy.com"
|
|
||||||
>Made with ❤️ by Ian Ramzy</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<!-- <li><a href="#">Contact</a></li>-->
|
|
||||||
<!-- <li><a href="#">About us</a></li>-->
|
|
||||||
<!-- <li><a href="#">FAQ's</a></li>-->
|
|
||||||
<!-- <li><a href="#">Support</a></li>-->
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<div class="footer-copyright">
|
|
||||||
© 2020 Zipcall, all rights reserved
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<script src="js/landing.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,136 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" class="no-js">
|
|
||||||
<head>
|
|
||||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
|
||||||
<script
|
|
||||||
async
|
|
||||||
src="https://www.googletagmanager.com/gtag/js?id=UA-162048272-1"
|
|
||||||
></script>
|
|
||||||
<script>
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
|
||||||
|
|
||||||
function gtag() {
|
|
||||||
dataLayer.push(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
gtag("js", new Date());
|
|
||||||
gtag("config", "UA-162048272-1");
|
|
||||||
</script>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
||||||
<title>Zipcall</title>
|
|
||||||
<link rel="stylesheet" href="css/landing.css" />
|
|
||||||
<link rel="shortcut icon" href="images/logo.svg" />
|
|
||||||
<meta property="og:title" content="Zipcall - Decentralized video calls" />
|
|
||||||
<meta
|
|
||||||
property="og:description"
|
|
||||||
content="Decentralized video
|
|
||||||
calling provides real-time HD quality and latency simply
|
|
||||||
not available with traditional technology."
|
|
||||||
/>
|
|
||||||
<meta property="og:image" content="https://zipcall.io/images/preview.png" />
|
|
||||||
<meta property="og:url" content="https://zipcall.io/" />
|
|
||||||
</head>
|
|
||||||
<body class="has-animations">
|
|
||||||
<div class="body-wrap">
|
|
||||||
<header class="site-header reveal-from-top">
|
|
||||||
<div class="container">
|
|
||||||
<div class="site-header-inner">
|
|
||||||
<div class="brand">
|
|
||||||
<h1 class="m-0">
|
|
||||||
<a href="/"
|
|
||||||
><img src="images/logo.svg" alt="Neon" width="32" height="32"
|
|
||||||
/></a>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main class="site-content">
|
|
||||||
<section class="hero section illustration-section-01">
|
|
||||||
<div class="container">
|
|
||||||
<div class="hero-inner section-inner">
|
|
||||||
<div class="split-wrap invert-mobile">
|
|
||||||
<div class="split-item">
|
|
||||||
<div
|
|
||||||
class="hero-content split-item-content center-content-mobile"
|
|
||||||
>
|
|
||||||
<h1
|
|
||||||
class="mt-0 mb-16 reveal-from-bottom"
|
|
||||||
data-reveal-delay="150"
|
|
||||||
>
|
|
||||||
Your browser is unsupported. Please open Zipcall in the
|
|
||||||
Safari app.
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
@media (min-width: 641px) {
|
|
||||||
.hero .split-wrap .split-item {
|
|
||||||
min-height: 492px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
<footer class="site-footer center-content-mobile">
|
|
||||||
<div class="container">
|
|
||||||
<div class="site-footer-inner">
|
|
||||||
<div class="footer-top space-between text-xxs">
|
|
||||||
<div class="brand">
|
|
||||||
<a href="/"
|
|
||||||
><img src="images/logo.svg" alt="Neon" width="32" height="32"
|
|
||||||
/></a>
|
|
||||||
</div>
|
|
||||||
<div class="footer-social">
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://github.com/ianramzy/decentralized-video-chat"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<title>GitHub</title>
|
|
||||||
<path
|
|
||||||
d="M7.95 0C3.578 0 0 3.578 0 7.95c0 3.479 2.286 6.46 5.466 7.553.397.1.497-.199.497-.397v-1.392c-2.187.497-2.683-.994-2.683-.994-.398-.894-.895-1.192-.895-1.192-.696-.497.1-.497.1-.497.795.1 1.192.795 1.192.795.696 1.292 1.888.894 2.286.696.1-.497.298-.895.497-1.093-1.79-.2-3.578-.895-3.578-3.976 0-.894.298-1.59.795-2.087-.1-.198-.397-.993.1-2.086 0 0 .695-.2 2.186.795a6.408 6.408 0 0 1 1.987-.299c.696 0 1.392.1 1.988.299 1.49-.994 2.186-.795 2.186-.795.398 1.093.199 1.888.1 2.086.496.597.795 1.292.795 2.087 0 3.081-1.889 3.677-3.677 3.876.298.398.596.895.596 1.59v2.187c0 .198.1.496.596.397C13.714 14.41 16 11.43 16 7.95 15.9 3.578 12.323 0 7.95 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="footer-bottom space-between text-xxs invert-order-desktop"
|
|
||||||
>
|
|
||||||
<nav class="footer-nav">
|
|
||||||
<ul class="list-reset">
|
|
||||||
<li>
|
|
||||||
<a target="_blank" href="https://ianramzy.com"
|
|
||||||
>Made with ❤️ by Ian Ramzy</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<!-- <li><a href="#">Contact</a></li>-->
|
|
||||||
<!-- <li><a href="#">About us</a></li>-->
|
|
||||||
<!-- <li><a href="#">FAQ's</a></li>-->
|
|
||||||
<!-- <li><a href="#">Support</a></li>-->
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<div class="footer-copyright">
|
|
||||||
© 2020 Zipcall, all rights reserved
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
<script src="js/landing.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
132
server.js
@ -1,132 +0,0 @@
|
|||||||
require("dotenv").config();
|
|
||||||
var sslRedirect = require("heroku-ssl-redirect");
|
|
||||||
// Get twillio auth and SID from heroku if deployed, else get from local .env file
|
|
||||||
var twillioAuthToken =
|
|
||||||
process.env.HEROKU_AUTH_TOKEN || process.env.LOCAL_AUTH_TOKEN;
|
|
||||||
var twillioAccountSID =
|
|
||||||
process.env.HEROKU_TWILLIO_SID || process.env.LOCAL_TWILLIO_SID;
|
|
||||||
var twilio = require("twilio")(twillioAccountSID, twillioAuthToken);
|
|
||||||
var express = require("express");
|
|
||||||
var app = express();
|
|
||||||
var http = require("http").createServer(app);
|
|
||||||
var io = require("socket.io")(http);
|
|
||||||
var path = require("path");
|
|
||||||
var public = path.join(__dirname, "public");
|
|
||||||
const url = require("url");
|
|
||||||
|
|
||||||
// enable ssl redirect
|
|
||||||
app.use(sslRedirect());
|
|
||||||
|
|
||||||
// Remove trailing slashes in url
|
|
||||||
app.use(function (req, res, next) {
|
|
||||||
if (req.path.substr(-1) === "/" && req.path.length > 1) {
|
|
||||||
let query = req.url.slice(req.path.length);
|
|
||||||
res.redirect(301, req.path.slice(0, -1) + query);
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/", function (req, res) {
|
|
||||||
res.sendFile(path.join(public, "landing.html"));
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/newcall", function (req, res) {
|
|
||||||
res.sendFile(path.join(public, "newcall.html"));
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/join/", function (req, res) {
|
|
||||||
res.redirect("/");
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/join/*", function (req, res) {
|
|
||||||
if (Object.keys(req.query).length > 0) {
|
|
||||||
logIt("redirect:" + req.url + " to " + url.parse(req.url).pathname);
|
|
||||||
res.redirect(url.parse(req.url).pathname);
|
|
||||||
} else {
|
|
||||||
res.sendFile(path.join(public, "chat.html"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/notsupported", function (req, res) {
|
|
||||||
res.sendFile(path.join(public, "notsupported.html"));
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/notsupportedios", function (req, res) {
|
|
||||||
res.sendFile(path.join(public, "notsupportedios.html"));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Serve static files in the public directory
|
|
||||||
app.use(express.static("public"));
|
|
||||||
|
|
||||||
// Simple logging function to add room name
|
|
||||||
function logIt(msg, room) {
|
|
||||||
if (room) {
|
|
||||||
console.log(room + ": " + msg);
|
|
||||||
} else {
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When a socket connects, set up the specific listeners we will use.
|
|
||||||
io.on("connection", function (socket) {
|
|
||||||
// When a client tries to join a room, only allow them if they are first or
|
|
||||||
// second in the room. Otherwise it is full.
|
|
||||||
socket.on("join", function (room) {
|
|
||||||
logIt("A client joined the room", room);
|
|
||||||
var clients = io.sockets.adapter.rooms[room];
|
|
||||||
var numClients = typeof clients !== "undefined" ? clients.length : 0;
|
|
||||||
if (numClients === 0) {
|
|
||||||
socket.join(room);
|
|
||||||
} else if (numClients === 1) {
|
|
||||||
socket.join(room);
|
|
||||||
// When the client is second to join the room, both clients are ready.
|
|
||||||
logIt("Broadcasting ready message", room);
|
|
||||||
// First to join call initiates call
|
|
||||||
socket.broadcast.to(room).emit("willInitiateCall", room);
|
|
||||||
socket.emit("ready", room).to(room);
|
|
||||||
socket.broadcast.to(room).emit("ready", room);
|
|
||||||
} else {
|
|
||||||
logIt("room already full", room);
|
|
||||||
socket.emit("full", room);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// When receiving the token message, use the Twilio REST API to request an
|
|
||||||
// token to get ephemeral credentials to use the TURN server.
|
|
||||||
socket.on("token", function (room) {
|
|
||||||
logIt("Received token request", room);
|
|
||||||
twilio.tokens.create(function (err, response) {
|
|
||||||
if (err) {
|
|
||||||
logIt(err, room);
|
|
||||||
} else {
|
|
||||||
logIt("Token generated. Returning it to the browser client", room);
|
|
||||||
socket.emit("token", response).to(room);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Relay candidate messages
|
|
||||||
socket.on("candidate", function (candidate, room) {
|
|
||||||
logIt("Received candidate. Broadcasting...", room);
|
|
||||||
socket.broadcast.to(room).emit("candidate", candidate);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Relay offers
|
|
||||||
socket.on("offer", function (offer, room) {
|
|
||||||
logIt("Received offer. Broadcasting...", room);
|
|
||||||
socket.broadcast.to(room).emit("offer", offer);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Relay answers
|
|
||||||
socket.on("answer", function (answer, room) {
|
|
||||||
logIt("Received answer. Broadcasting...", room);
|
|
||||||
socket.broadcast.to(room).emit("answer", answer);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen for Heroku port, otherwise just use 3000
|
|
||||||
var port = process.env.PORT || 3000;
|
|
||||||
http.listen(port, function () {
|
|
||||||
console.log("http://localhost:" + port);
|
|
||||||
});
|
|