├── .gitignore
├── assets
├── gh-light.png
├── visual_functions.js
├── style.css
├── index.html
├── updater_functions.js
└── progressbar.min.js
├── .replit
├── .babelrc
├── start.js
├── .vscode
└── launch.json
├── package.json
├── .eslintrc.js
├── LICENSE
├── config-default.js
├── discord.js
├── README.md
├── irc.js
├── server.js
└── common.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | config.js
3 | *.pem
4 | data.json*
5 |
--------------------------------------------------------------------------------
/assets/gh-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rb28z2/progress-bot/HEAD/assets/gh-light.png
--------------------------------------------------------------------------------
/.replit:
--------------------------------------------------------------------------------
1 | language = "nodejs"
2 | run = "test -f config.js || cp config-default.js config.js && sed -i 's/80/1080/g' config.js && node start.js"
3 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | ["transform-runtime", {
4 | "polyfill": false,
5 | "regenerator": true
6 | }]
7 | ]
8 | }
--------------------------------------------------------------------------------
/start.js:
--------------------------------------------------------------------------------
1 | // Transpile all code following this line with babel and use 'env' (aka ES6) preset.
2 | require("babel-register")({
3 | presets: [ "env" ]
4 | });
5 |
6 | // Import the rest of our application.
7 | module.exports = require("./server.js");
8 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "program": "${workspaceFolder}/start.js"
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "progressBot",
3 | "version": "0.0.1",
4 | "description": "hi-tech weaponized moe progress delivery",
5 | "dependencies": {
6 | "colors": "^1.1.2",
7 | "discord.js": "^11.4.2",
8 | "express": "^4.15.2",
9 | "irc": "^0.5.2",
10 | "jsonfile": "^6.0.1",
11 | "password-prompt": "^1.1.2",
12 | "socket.io": "^2.0.1"
13 | },
14 | "devDependencies": {
15 | "babel-plugin-transform-runtime": "^6.23.0",
16 | "babel-preset-env": "^1.7.0",
17 | "babel-register": "^6.26.0",
18 | "eslint": "^6.7.2",
19 | "nodemon": "^2.0.3"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/assets/visual_functions.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | $("#plug").hover(function()
3 | {
4 | console.log("Hover");
5 | $("#plug-text").fadeIn();
6 | }, function()
7 | {
8 | $("#plug-text").stop().delay(3000).fadeOut();
9 | }
10 | );
11 |
12 | var original;
13 |
14 | $("#pb-qc").hover(function()
15 | {
16 | original = $("#pb-qc > div").text();
17 | $("#pb-qc > div").text("Quality Checking");
18 | }, function()
19 | {
20 | $("#pb-qc > div").text(original);
21 | }
22 | );
23 |
24 | $("#pb-tlc").hover(function()
25 | {
26 | original = $("#pb-tlc > div").text();
27 | $("#pb-tlc > div").text("Translation Checking");
28 | }, function()
29 | {
30 | $("#pb-tlc > div").text(original);
31 | }
32 | );
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true
4 | },
5 | "extends": "eslint:recommended",
6 | "parserOptions": {
7 | "ecmaVersion": 2017,
8 | "sourceType":"module"
9 | },
10 | "rules": {
11 | "indent": [
12 | "error",
13 | "tab"
14 | ],
15 | "linebreak-style": [
16 | "error",
17 | "unix"
18 | ],
19 | "quotes": [
20 | "error",
21 | "double"
22 | ],
23 | "semi": [
24 | "error",
25 | "always"
26 | ],
27 | "no-console": [
28 | "off"
29 | ],
30 | "no-unused-vars": [
31 | "warn"
32 | ]
33 | }
34 | };
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 rb28z2
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/assets/style.css:
--------------------------------------------------------------------------------
1 | #outer-container {
2 | width: 100%;
3 | font-family: "Open Sans", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | .pb-div {
7 | font-family: "Open Sans", Helvetica, Arial, sans-serif;
8 | height: 10vh;
9 | }
10 |
11 | #pb-title {
12 | font-size: 5vh;
13 | position: relative;
14 | font-weight: 600;
15 | height: auto;
16 | text-align: center;
17 | color: #989898;
18 | }
19 |
20 | #pb-episode {
21 | height: 3vh;
22 | font-size: 3vh;
23 | text-align: center;
24 | color: #989898;
25 | }
26 |
27 | #inner-container{
28 | margin-top: 1.5rem;
29 | }
30 |
31 | svg {
32 | border-radius: 5px;
33 | }
34 |
35 | .footer-text {
36 | font-size: x-small;
37 | color: grey;
38 | float: right;
39 | }
40 |
41 | #totalUsers {
42 | text-align: right;
43 | float: right;
44 | }
45 |
46 | #plug {
47 | width: auto;
48 | height: 32px;
49 | overflow: hidden;
50 | color: grey;
51 | float: left;
52 | }
53 |
54 | #plug-text {
55 | margin-left: 10px;
56 | display: none;
57 | float: right;
58 | margin-top: -3px;
59 | }
60 |
61 | a {
62 | color: #287fb8;
63 | font-size: 80%;
64 | }
65 |
66 | #plug > img {
67 | opacity: 0.7;
68 | width: 15px;
69 | }
70 |
71 | #footer {
72 | margin-top: -5px;
73 | }
74 |
75 | body {
76 | background: #292929;
77 | }
78 |
--------------------------------------------------------------------------------
/config-default.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | enableIrc: true,
3 | /*
4 | List all channels the bot should join, include channel keys if required.
5 | ex: ["#channelA", "#channelB channelB-password", "#anotherChannel"]
6 | */
7 | channels: ["#yourchannel"],
8 | /*
9 | List of channels (a subset of 'channels') that the bot should listen for commands on.
10 | Note that everyone in that channel will be able to trigger commands.
11 | */
12 | listenChannel: ["#yourchannel"],
13 | /*
14 | List of channels (a subset of 'channels') that the bot should announce updates on.
15 | Bot will NOT respond to trigger commands here.
16 | */
17 | notifyChannel: ["#yourchannel"],
18 | server: "irc.server.here",
19 | botName: "progressBot",
20 | port: 80,
21 | httpsMode: false, //enables https only mode
22 | httpsPort: 8443,
23 | httpsKey: "/path/to/key.pem", //port, key, and cert not required in http mode
24 | httpsCert: "/path/to/cert.pem",
25 | trigger: "!pb ", //Word to trigger actions. IMPORTANT: INCLUDE A TRAILING SPACE
26 | identify: false, //Set to true to enable nickserv identification
27 | nick_secret: false, //set to a "" enclosed password if you dont want to enter it every time
28 | // else leave false to prompt for a password
29 | nickserv: "nickserv", //nick identification service's name
30 |
31 |
32 | enableDiscord: false,
33 | discordKey: "yourkeyhere", // your discord bot token
34 | discordNotifyChannels: ["0"], // comma separated numerical notify channel id
35 | discordListenChannels: ["0"]
36 | };
37 |
38 | export default config;
39 |
--------------------------------------------------------------------------------
/assets/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Progress Bot
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/discord.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-unused-vars
2 | import colors from "colors";
3 |
4 | import Discord from "discord.js";
5 | const client = new Discord.Client();
6 | import config from "./config.js";
7 | import { io as _io, triggerMatch, getMsg, getCommand, getValue, validCommands, newTitleTrigger, getStats, getDiscordtoSay, getIRCtoSay } from "./common.js";
8 | import { ircSay } from "./irc.js";
9 |
10 | const stats = getStats();
11 | const io = _io();
12 |
13 |
14 | export function initDiscord() {
15 | client.on("ready", () => {
16 | console.log(`Logged in as ${client.user.tag}`.yellow);
17 | });
18 |
19 | let lastUpdated = exports.lastUpdated;
20 |
21 | client.on("message", msg => {
22 | let authenticated = false;
23 |
24 | if (config.discordListenChannels.includes(msg.channel.id))
25 | authenticated = true;
26 |
27 | if (triggerMatch(msg.content) && authenticated) {
28 | const message = getMsg(msg.content);
29 | const command = getCommand(message);
30 | const value = getValue(message);
31 |
32 | if (validCommands.includes(command)) {
33 | newTitleTrigger(command, value);
34 | console.log("Valid command: ".yellow, command, value);
35 | stats[command] = value;
36 |
37 | let discordMessage = getDiscordtoSay(command);
38 | let ircMessage = getIRCtoSay(command);
39 | if (config.enableDiscord && discordMessage) discordSay(discordMessage);
40 | if (config.enableIrc && ircMessage) ircSay(ircMessage);
41 |
42 |
43 | io.emit("update-stats", {
44 | "command": command,
45 | "value": value
46 | });
47 | lastUpdated = new Date().toUTCString();
48 | io.emit("date-update", lastUpdated);
49 | }
50 | }
51 | });
52 |
53 | client.on("error", error => {
54 | console.log(error);
55 | });
56 |
57 | client.login(config.discordKey);
58 | }
59 |
60 | export function discordSay(message) {
61 | config.discordNotifyChannels.forEach( async value => {
62 | let channel = client.channels.get(value);
63 | await channel.send(message);
64 | });
65 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://codeclimate.com/github/rb28z2/progress-bot/maintainability)
2 | [](https://repl.it/github/rb28z2/progress-bot)
3 |
4 | # progress-bot
5 | High-tech weaponized moe progress delivery bot for IRC, Discord, and web. Uses NodeJS and socket.io to update progress from IRC/Discord messages onto a web interface. The web interface can also be embedded on another page to provide instant and live progress updates.
6 |
7 | # Screenshots
8 | 
9 |
10 | # Installation
11 | ##### Requirements
12 | * nodejs
13 | * npm
14 | * build-essential
15 | * libicu-dev
16 |
17 | ##### Instructions
18 | Install `build-essential` and `libicu-dev` via your package manager, then run `npm install` to install the required dependencies. Then, copy `config-default.js` to `config.js` and edit as required. Finally, run the bot with `node start.js`.
19 |
20 | ##### Compatibility
21 | In theory, this should work anywhere node (and the above dependencies) works. However I've only tested it on Linux (Ubuntu 16.04), and Windows 10.
22 |
23 | # Usage
24 | ##### Available commands and syntax
25 |
26 | Assuming a trigger word of `!pb`:
27 |
28 |
29 | | Command | Syntax | Example |
30 | | ------------- | ----------------- | ------------------------ |
31 | | Title | `title ` | `!pb title A very cool show` |
32 | | Episode | `episode ` | `!pb episode 12/25` |
33 | | Encoding | `encode ` | `!pb encode 30` |
34 | | Timing | `time ` | `!pb time 10` |
35 | | Translation | `tl ` | `!pb tl 100` |
36 | | Translation Check | `tlc ` | `!pb tlc 20` |
37 | | Typesetting | `ts ` | `!pb ts 44` |
38 | | Editing | `edit ` | `!pb edit 0` |
39 | | Quality Check | `qc ` | `!pb qc 67` |
40 |
41 | Open a browser to `:80` (port 80 by default) to see the live changes.
42 |
43 | # Demo (front end only)
44 |
45 | See a live, in-production setup here: https://asenshi.moe:8443
46 |
--------------------------------------------------------------------------------
/assets/updater_functions.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | var socket = io();
3 |
4 | console.log("Starting");
5 | var stats = {};
6 | //var [encode], [title], [episode], [time], [tl], [ts], [edit], [qc];
7 |
8 |
9 | socket.on("init-stats", function(val) {
10 | if (val["command"] !== "title" && val["command"] !== "episode") {
11 | //console.log(val["command"]);
12 |
13 | var dispCommand = "";
14 | if (val["command"] === "tl")
15 | dispCommand = "Translation";
16 | else if (val["command"] === "edit")
17 | dispCommand = "Editing";
18 | else if (val["command"] === "ts")
19 | dispCommand = "Typesetting";
20 | else if (val["command"] === "time")
21 | dispCommand = "Timing";
22 | else if (val["command"] === "encode")
23 | dispCommand = "Encoding";
24 | else if (val["command"] === "tlc")
25 | dispCommand = "TLC";
26 | else if (val["command"] === "qc")
27 | dispCommand = "QC";
28 |
29 | if (!stats[val["command"]]) {
30 | stats[val["command"]] = new ProgressBar.Line("#pb-" + val["command"], {
31 | easing: "easeInOut",
32 | color: "#287fb8",
33 | trailColor: "#555",
34 | trailWidth: 1,
35 | svgStyle: {
36 | width: "100%",
37 | height: "50%"
38 | },
39 | text: {
40 | style: {
41 | autoStyleContainer: false,
42 | color: "#fff",
43 | fontFamily: "\"Open Sans\", Helvetica, Arial, sans-serif",
44 | fontSize: "3.4vh",
45 | position: "absolute",
46 | width: "100%",
47 | textAlign: "center",
48 | fontWeight: "600",
49 | top: "0%"
50 | }
51 | },
52 | step: (state, bar) => {
53 | bar.setText(dispCommand + ": " + Math.round(bar.value() * 100) + "%");
54 | }
55 | });
56 | }
57 | stats[val["command"]].animate(val["value"]);
58 | } else {
59 | console.log(val["value"]);
60 | if (val["command"] === "title") {
61 | $("#pb-title").text(val["value"]);
62 | } else {
63 | $("#pb-episode").text("Episode: " + val["value"]);
64 | }
65 | }
66 | });
67 |
68 |
69 | socket.on("update-stats", function(val) {
70 | console.log("Updating");
71 | if (val["command"] !== "title" && val["command"] !== "episode") {
72 | stats[val["command"]].animate(val["value"] / 100);
73 | } else {
74 | if (val["command"] === "title") {
75 | $("#pb-title").text(val["value"]);
76 | } else {
77 | $("#pb-episode").text("Episode: " + val["value"]);
78 | }
79 | }
80 |
81 | });
82 |
83 | socket.on("update-users", function(val) {
84 | $("#totalUsers").text("Users: " + val);
85 | });
86 |
87 | socket.on("date-update", function(val) {
88 | $("#update").text(val);
89 | });
--------------------------------------------------------------------------------
/irc.js:
--------------------------------------------------------------------------------
1 | import irc from "irc";
2 | import config from "./config.js";
3 | import { io as _io, getStats, validCommands, getCommand, getValue, triggerMatch, getMsg, newTitleTrigger, getIRCtoSay, getDiscordtoSay} from "./common.js";
4 | import { discordSay } from "./discord.js";
5 |
6 | const io = _io();
7 | const stats = getStats();
8 | let bot;
9 |
10 | export function initIRC() {
11 | console.log("Connecting to IRC...".green);
12 | bot = new irc.Client(config.server, config.botName, {
13 | channels: config.channels
14 | });
15 | console.log("Connected!".yellow);
16 |
17 | authorize();
18 |
19 | let lastUpdated = exports.lastUpdated;
20 |
21 | const listener = `message${config.listenChannel[0]}`;
22 |
23 | console.log("Adding listener for trigger...".green);
24 | /**
25 | * Below block is for listening to a specific trigger word.
26 | */
27 | bot.addListener(listener, (from, text, message) => {
28 | //extract the first n characters from each message and check if it matches the trigger word
29 | if (triggerMatch(text)) {
30 | const msg = getMsg(text);
31 | console.log("Message Received: ", msg);
32 | //if we have a matching trigger, extract the command the value
33 | const command = getCommand(msg);
34 | const value = getValue(msg);
35 |
36 |
37 | if (validCommands.includes(command)) {
38 |
39 | newTitleTrigger(command, value);
40 | console.log("Valid command: ".yellow, command, value);
41 | stats[command] = value;
42 |
43 | let ircMessage = getIRCtoSay(command);
44 | let discordMessage = getDiscordtoSay(command);
45 |
46 | if (config.enableDiscord && discordMessage) ircSay(ircMessage);
47 | if (config.enableIrc && ircMessage) discordSay(discordMessage);
48 | }
49 |
50 | io.emit("update-stats", {
51 | "command": command,
52 | "value": value
53 | });
54 | lastUpdated = new Date().toUTCString();
55 | io.emit("date-update", lastUpdated);
56 |
57 | }
58 | });
59 |
60 | bot.addListener("error", message => {
61 | console.log("IRC Error ".red, message);
62 | });
63 |
64 |
65 |
66 | async function authorize() {
67 | if (config.identify) {
68 | console.log("Identify nick enabled".yellow);
69 | if (config.nick_secret) {
70 | console.log("Password found".green);
71 | let password = config.nick_secret;
72 | bot.say(config.nickserv, `identify ${password}`);
73 | }
74 | else {
75 | console.log("Prompting for password".yellow);
76 | let pass_prompt = require("password-prompt");
77 | let password = await pass_prompt("ENTER PASSWORD AT ANY TIME");
78 | bot.say(config.nickserv, `identify ${password}`);
79 | }
80 | console.log("Identified".green); //todo: check if identify was successful
81 | }
82 | }
83 | }
84 |
85 | export function ircSay(message) {
86 | if (message){
87 | for (let i = 0; i < config.notifyChannel.length; i++) {
88 | bot.say(config.notifyChannel[i], message);
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | import config from "./config.js";
2 | import fs from "fs";
3 |
4 | //below block is to init stuff. nothing special here
5 | import colors from "colors";
6 |
7 | console.log("Initializing Express and HTTP stuff...".green);
8 | import express from "express";
9 |
10 | const app = express();
11 | app.use(express.static("assets")); //deliver content in the 'assets' folder
12 |
13 | let http = require("http")
14 | .Server(app);
15 |
16 | import {getStats, initIo, lastUpdated, validCommands, file } from "./common.js";
17 | let stats = getStats();
18 |
19 |
20 | //configure in HTTPS mode
21 | if (config.httpsMode) {
22 | console.log("HTTPS Mode".yellow);
23 | http = require("https");
24 | http = http.createServer({
25 | key: fs.readFileSync(config.httpsKey),
26 | cert: fs.readFileSync(config.httpsCert)
27 | }, app);
28 | }
29 |
30 | console.log("Initializing Socket.io stuff...".green);
31 |
32 | const io = initIo(http); // socket.io for realtime stuff
33 |
34 |
35 | import jsonFile from "jsonfile"; //because i'm lazy
36 |
37 | if (config.enableIrc) {
38 | let ircClient = require("./irc.js");
39 | ircClient.initIRC();
40 | }
41 |
42 | if (config.enableDiscord) {
43 | let discordClient = require("./discord.js");
44 | discordClient.initDiscord();
45 | }
46 |
47 | console.log("\nINIT COMPLETE\n".bold.magenta);
48 |
49 | console.log(colors.grey("%s\n"), JSON.stringify(stats));
50 |
51 |
52 | app.get("/", (req, res) => {
53 | res.sendFile(`${__dirname}/index.html`);
54 | });
55 |
56 | app.get("/progressbar.min.js", (req, res) => {
57 | res.sendFile(`${__dirname}/progressbar.min.js`);
58 | });
59 |
60 | io.on("connection", socket => {
61 | console.log("Socket connection established. ID: ".grey, socket.id);
62 | socket.emit("irc message", "Connected!");
63 |
64 | socket.emit("date-update", lastUpdated);
65 |
66 |
67 |
68 | //for each new client, update their stats (initial update)
69 | for (let i = 0; i < validCommands.length; i++) {
70 | let command = validCommands[i];
71 | //console.log(command);
72 | if (command !== "title" && command !== "episode") {
73 | socket.emit("init-stats", {
74 | "command": validCommands[i],
75 | "value": stats[validCommands[i]] / 100
76 | });
77 | }
78 | else {
79 | socket.emit("init-stats", {
80 | "command": validCommands[i],
81 | "value": stats[validCommands[i]]
82 | });
83 | }
84 | }
85 |
86 | io.emit("update-users", io.engine.clientsCount);
87 |
88 | socket.on("disconnect", socket => {
89 | io.emit("update-users", io.engine.clientsCount);
90 | });
91 |
92 | });
93 |
94 |
95 | if (config.httpsMode) {
96 | http.listen(8443, () => {
97 | console.log("Listening on port %s in HTTPS mode".bold.yellow, config.httpsPort);
98 | });
99 | }
100 | else {
101 | http.listen(config.port, () => {
102 | console.log("Listening on port %s in HTTP mode".bold.yellow, config.port);
103 | });
104 | }
105 | //Below stuff is all for clean exits and for uncaught exception handling
106 | process.stdin.resume(); //so the program will not close instantly
107 |
108 | function exitHandler({cleanup, exit}, err) {
109 | if (cleanup) console.log("clean".red);
110 | if (err) console.log(err.stack);
111 |
112 | jsonFile.writeFileSync(file, stats);
113 | console.log("Saving stats to disk".yellow);
114 |
115 | if (exit) process.exit();
116 | }
117 |
118 | //do something when app is closing
119 | process.on("exit", exitHandler.bind(null, {
120 | cleanup: true
121 | }));
122 |
123 | //catches ctrl+c event
124 | process.on("SIGINT", exitHandler.bind(null, {
125 | exit: true
126 | }));
127 |
128 | //catches uncaught exceptions
129 | process.on("uncaughtException", exitHandler.bind(null, {
130 | exit: true
131 | }));
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/common.js:
--------------------------------------------------------------------------------
1 | import jsonFile from "jsonfile";
2 | import config from "./config.js";
3 | let ioInstance;
4 | let stats;
5 |
6 | const encode = "encode";
7 | const title = "title";
8 | const tlc = "tlc";
9 | const episode = "episode";
10 | const time = "time";
11 | const tl = "tl";
12 | const ts = "ts";
13 | const edit = "edit";
14 | const qc = "qc";
15 |
16 | export function initIo(http) {
17 | ioInstance = require("socket.io")(http);
18 | return ioInstance;
19 | }
20 |
21 | export function io() {
22 | return ioInstance;
23 | }
24 |
25 | export var lastUpdated = new Date().toUTCString();
26 | export var validCommands = ["encode", "tlc", "title", "episode", "time", "tl", "ts", "edit", "qc"];
27 |
28 | export function getStats() {
29 | if (!stats){
30 | console.log("Reading existing data...".green);
31 | const file = `${__dirname}/data.json`;
32 | try {
33 | stats = jsonFile.readFileSync(file);
34 | }
35 | catch (err) {
36 | if (err.code === "ENOENT") {
37 | //If no data file was found, start with dummy data
38 | console.log("No default data file found".yellow);
39 | console.log("Creating dummy data".yellow);
40 | stats = {
41 | "encode": 0,
42 | "title": "Another show",
43 | "episode": "5/12",
44 | "time": "20",
45 | "tl": "50",
46 | "ts": 0,
47 | "edit": "50",
48 | "qc": "60",
49 | "tlc": "20"
50 | };
51 | }
52 | }
53 | }
54 | return stats;
55 | }
56 |
57 | export var file = `${__dirname}/data.json`;
58 |
59 | export function saveStats() {
60 | jsonFile.writeFileSync(file, getStats());
61 | console.log("Saving stats to disk".yellow);
62 | }
63 |
64 | setInterval(saveStats, 1000 * 60 * 10); // save stats every 10 minutes
65 |
66 | export function triggerMatch(text) {
67 | return text.substring(0, config.trigger.length) === config.trigger;
68 | }
69 |
70 | export function getMsg(text) {
71 | return text.substring(config.trigger.length);
72 | }
73 |
74 | export function getCommand(msg) {
75 | return msg.substring(0, msg.indexOf(" "));
76 | }
77 |
78 | export function getValue(msg) {
79 | return msg.substring(msg.indexOf(" ") + 1);
80 | }
81 |
82 | export function newTitleTrigger(command, value) {
83 | const tempTitle = stats["title"];
84 | if (command === "title" || command === "episode") {
85 | console.log("Resetting everything".yellow);
86 | for (const key in stats) {
87 | if (stats.hasOwnProperty(key)) {
88 | stats[key] = 0;
89 | ioInstance.emit("update-stats", {
90 | "command": key,
91 | "value": 0
92 | });
93 | }
94 | }
95 |
96 | if (command === "episode") {
97 | stats["title"] = tempTitle;
98 | stats["episode"] = value;
99 | ioInstance.emit("update-stats", {
100 | "command": "title",
101 | "value": tempTitle
102 | });
103 | }
104 | }
105 |
106 | }
107 |
108 | export function getIRCtoSay(command) {
109 | if (command === "episode") {
110 | return `Currently working on \u0002${stats[title]}\u0002 episode ${stats[episode]}`;
111 | }
112 | else if (command !== "title") {
113 | return `\u0002${stats[title]}\u0002 | Episode ${stats[episode]} | ${capitalizeFirst(command)} progress @ ${stats[command]}%`;
114 | }
115 | else return null;
116 | }
117 |
118 | export function getDiscordtoSay(command) {
119 | if (command === "episode") {
120 | return `Currently working on **${stats[title]}** episode ${stats[episode]}`;
121 | }
122 | else if (command !== "title") {
123 | return `**${stats[title]}** | Episode ${stats[episode]} | ${capitalizeFirst(command)} progress @ ${stats[command]}%`;
124 | }
125 | else return null;
126 | }
127 |
128 | function capitalizeFirst(string) {
129 | if (string.length > 3) {
130 | return string.charAt(0).toUpperCase() + string.slice(1);
131 | }
132 | else {
133 | return string.toUpperCase();
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/assets/progressbar.min.js:
--------------------------------------------------------------------------------
1 | // ProgressBar.js 1.0.1
2 | // https://kimmobrunfeldt.github.io/progressbar.js
3 | // License: MIT
4 |
5 | !function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.ProgressBar=a()}}(function(){var a;return function b(a,c,d){function e(g,h){if(!c[g]){if(!a[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};a[g][0].call(k.exports,function(b){var c=a[g][1][b];return e(c?c:b)},k,k.exports,b,a,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;ga?0:(a-f)/e;for(h in b)b.hasOwnProperty(h)&&(i=g[h],k="function"==typeof i?i:o[i],b[h]=j(c[h],d[h],k,l));return b}function j(a,b,c,d){return a+(b-a)*c(d)}function k(a,b){var c=n.prototype.filter,d=a._filterArgs;f(c,function(e){"undefined"!=typeof c[e][b]&&c[e][b].apply(a,d)})}function l(a,b,c,d,e,f,g,h,j,l,m){v=b+c+d,w=Math.min(m||u(),v),x=w>=v,y=d-(v-w),a.isPlaying()&&(x?(j(g,a._attachment,y),a.stop(!0)):(a._scheduleId=l(a._timeoutHandler,s),k(a,"beforeTween"),b+c>w?i(1,e,f,g,1,1,h):i(w,e,f,g,d,b+c,h),k(a,"afterTween"),j(e,a._attachment,y)))}function m(a,b){var c={},d=typeof b;return"string"===d||"function"===d?f(a,function(a){c[a]=b}):f(a,function(a){c[a]||(c[a]=b[a]||q)}),c}function n(a,b){this._currentState=a||{},this._configured=!1,this._scheduleFunction=p,"undefined"!=typeof b&&this.setConfig(b)}var o,p,q="linear",r=500,s=1e3/60,t=Date.now?Date.now:function(){return+new Date},u="undefined"!=typeof SHIFTY_DEBUG_NOW?SHIFTY_DEBUG_NOW:t;p="undefined"!=typeof window?window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||window.mozCancelRequestAnimationFrame&&window.mozRequestAnimationFrame||setTimeout:setTimeout;var v,w,x,y;return n.prototype.tween=function(a){return this._isTweening?this:(void 0===a&&this._configured||this.setConfig(a),this._timestamp=u(),this._start(this.get(),this._attachment),this.resume())},n.prototype.setConfig=function(a){a=a||{},this._configured=!0,this._attachment=a.attachment,this._pausedAtTime=null,this._scheduleId=null,this._delay=a.delay||0,this._start=a.start||e,this._step=a.step||e,this._finish=a.finish||e,this._duration=a.duration||r,this._currentState=g({},a.from)||this.get(),this._originalState=this.get(),this._targetState=g({},a.to)||this.get();var b=this;this._timeoutHandler=function(){l(b,b._timestamp,b._delay,b._duration,b._currentState,b._originalState,b._targetState,b._easing,b._step,b._scheduleFunction)};var c=this._currentState,d=this._targetState;return h(d,c),this._easing=m(c,a.easing||q),this._filterArgs=[c,this._originalState,d,this._easing],k(this,"tweenCreated"),this},n.prototype.get=function(){return g({},this._currentState)},n.prototype.set=function(a){this._currentState=a},n.prototype.pause=function(){return this._pausedAtTime=u(),this._isPaused=!0,this},n.prototype.resume=function(){return this._isPaused&&(this._timestamp+=u()-this._pausedAtTime),this._isPaused=!1,this._isTweening=!0,this._timeoutHandler(),this},n.prototype.seek=function(a){a=Math.max(a,0);var b=u();return this._timestamp+a===0?this:(this._timestamp=b-a,this.isPlaying()||(this._isTweening=!0,this._isPaused=!1,l(this,this._timestamp,this._delay,this._duration,this._currentState,this._originalState,this._targetState,this._easing,this._step,this._scheduleFunction,b),this.pause()),this)},n.prototype.stop=function(a){return this._isTweening=!1,this._isPaused=!1,this._timeoutHandler=e,(b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.oCancelAnimationFrame||b.msCancelAnimationFrame||b.mozCancelRequestAnimationFrame||b.clearTimeout)(this._scheduleId),a&&(k(this,"beforeTween"),i(1,this._currentState,this._originalState,this._targetState,1,0,this._easing),k(this,"afterTween"),k(this,"afterTweenEnd"),this._finish.call(this,this._currentState,this._attachment)),this},n.prototype.isPlaying=function(){return this._isTweening&&!this._isPaused},n.prototype.setScheduleFunction=function(a){this._scheduleFunction=a},n.prototype.dispose=function(){var a;for(a in this)this.hasOwnProperty(a)&&delete this[a]},n.prototype.filter={},n.prototype.formula={linear:function(a){return a}},o=n.prototype.formula,g(n,{now:u,each:f,tweenProps:i,tweenProp:j,applyFilter:k,shallowCopy:g,defaults:h,composeEasingObject:m}),"function"==typeof SHIFTY_DEBUG_NOW&&(b.timeoutHandler=l),"object"==typeof d?c.exports=n:"function"==typeof a&&a.amd?a(function(){return n}):"undefined"==typeof b.Tweenable&&(b.Tweenable=n),n}();!function(){e.shallowCopy(e.prototype.formula,{easeInQuad:function(a){return Math.pow(a,2)},easeOutQuad:function(a){return-(Math.pow(a-1,2)-1)},easeInOutQuad:function(a){return(a/=.5)<1?.5*Math.pow(a,2):-.5*((a-=2)*a-2)},easeInCubic:function(a){return Math.pow(a,3)},easeOutCubic:function(a){return Math.pow(a-1,3)+1},easeInOutCubic:function(a){return(a/=.5)<1?.5*Math.pow(a,3):.5*(Math.pow(a-2,3)+2)},easeInQuart:function(a){return Math.pow(a,4)},easeOutQuart:function(a){return-(Math.pow(a-1,4)-1)},easeInOutQuart:function(a){return(a/=.5)<1?.5*Math.pow(a,4):-.5*((a-=2)*Math.pow(a,3)-2)},easeInQuint:function(a){return Math.pow(a,5)},easeOutQuint:function(a){return Math.pow(a-1,5)+1},easeInOutQuint:function(a){return(a/=.5)<1?.5*Math.pow(a,5):.5*(Math.pow(a-2,5)+2)},easeInSine:function(a){return-Math.cos(a*(Math.PI/2))+1},easeOutSine:function(a){return Math.sin(a*(Math.PI/2))},easeInOutSine:function(a){return-.5*(Math.cos(Math.PI*a)-1)},easeInExpo:function(a){return 0===a?0:Math.pow(2,10*(a-1))},easeOutExpo:function(a){return 1===a?1:-Math.pow(2,-10*a)+1},easeInOutExpo:function(a){return 0===a?0:1===a?1:(a/=.5)<1?.5*Math.pow(2,10*(a-1)):.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return-(Math.sqrt(1-a*a)-1)},easeOutCirc:function(a){return Math.sqrt(1-Math.pow(a-1,2))},easeInOutCirc:function(a){return(a/=.5)<1?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)},easeOutBounce:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},easeInBack:function(a){var b=1.70158;return a*a*((b+1)*a-b)},easeOutBack:function(a){var b=1.70158;return(a-=1)*a*((b+1)*a+b)+1},easeInOutBack:function(a){var b=1.70158;return(a/=.5)<1?.5*(a*a*(((b*=1.525)+1)*a-b)):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},elastic:function(a){return-1*Math.pow(4,-8*a)*Math.sin((6*a-1)*(2*Math.PI)/2)+1},swingFromTo:function(a){var b=1.70158;return(a/=.5)<1?.5*(a*a*(((b*=1.525)+1)*a-b)):.5*((a-=2)*a*(((b*=1.525)+1)*a+b)+2)},swingFrom:function(a){var b=1.70158;return a*a*((b+1)*a-b)},swingTo:function(a){var b=1.70158;return(a-=1)*a*((b+1)*a+b)+1},bounce:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},bouncePast:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?2-(7.5625*(a-=1.5/2.75)*a+.75):2.5/2.75>a?2-(7.5625*(a-=2.25/2.75)*a+.9375):2-(7.5625*(a-=2.625/2.75)*a+.984375)},easeFromTo:function(a){return(a/=.5)<1?.5*Math.pow(a,4):-.5*((a-=2)*Math.pow(a,3)-2)},easeFrom:function(a){return Math.pow(a,4)},easeTo:function(a){return Math.pow(a,.25)}})}(),function(){function a(a,b,c,d,e,f){function g(a){return((n*a+o)*a+p)*a}function h(a){return((q*a+r)*a+s)*a}function i(a){return(3*n*a+2*o)*a+p}function j(a){return 1/(200*a)}function k(a,b){return h(m(a,b))}function l(a){return a>=0?a:0-a}function m(a,b){var c,d,e,f,h,j;for(e=a,j=0;8>j;j++){if(f=g(e)-a,l(f)e)return c;if(e>d)return d;for(;d>c;){if(f=g(e),l(f-a)f?c=e:d=e,e=.5*(d-c)+c}return e}var n=0,o=0,p=0,q=0,r=0,s=0;return p=3*b,o=3*(d-b)-p,n=1-p-o,s=3*c,r=3*(e-c)-s,q=1-s-r,k(a,j(f))}function b(b,c,d,e){return function(f){return a(f,b,c,d,e,1)}}e.setBezierFunction=function(a,c,d,f,g){var h=b(c,d,f,g);return h.displayName=a,h.x1=c,h.y1=d,h.x2=f,h.y2=g,e.prototype.formula[a]=h},e.unsetBezierFunction=function(a){delete e.prototype.formula[a]}}(),function(){function a(a,b,c,d,f,g){return e.tweenProps(d,b,a,c,1,g,f)}var b=new e;b._filterArgs=[],e.interpolate=function(c,d,f,g,h){var i=e.shallowCopy({},c),j=h||0,k=e.composeEasingObject(c,g||"linear");b.set({});var l=b._filterArgs;l.length=0,l[0]=i,l[1]=c,l[2]=d,l[3]=k,e.applyFilter(b,"tweenCreated"),e.applyFilter(b,"beforeTween");var m=a(c,i,d,f,k,j);return e.applyFilter(b,"afterTween"),m}}(),function(a){function b(a,b){var c,d=[],e=a.length;for(c=0;e>c;c++)d.push("_"+b+"_"+c);return d}function c(a){var b=a.match(v);return b?(1===b.length||a[0].match(u))&&b.unshift(""):b=["",""],b.join(A)}function d(b){a.each(b,function(a){var c=b[a];"string"==typeof c&&c.match(z)&&(b[a]=e(c))})}function e(a){return i(z,a,f)}function f(a){var b=g(a);return"rgb("+b[0]+","+b[1]+","+b[2]+")"}function g(a){return a=a.replace(/#/,""),3===a.length&&(a=a.split(""),a=a[0]+a[0]+a[1]+a[1]+a[2]+a[2]),B[0]=h(a.substr(0,2)),B[1]=h(a.substr(2,2)),B[2]=h(a.substr(4,2)),B}function h(a){return parseInt(a,16)}function i(a,b,c){var d=b.match(a),e=b.replace(a,A);if(d)for(var f,g=d.length,h=0;g>h;h++)f=d.shift(),e=e.replace(A,c(f));return e}function j(a){return i(x,a,k)}function k(a){for(var b=a.match(w),c=b.length,d=a.match(y)[0],e=0;c>e;e++)d+=parseInt(b[e],10)+",";return d=d.slice(0,-1)+")"}function l(d){var e={};return a.each(d,function(a){var f=d[a];if("string"==typeof f){var g=r(f);e[a]={formatString:c(f),chunkNames:b(g,a)}}}),e}function m(b,c){a.each(c,function(a){for(var d=b[a],e=r(d),f=e.length,g=0;f>g;g++)b[c[a].chunkNames[g]]=+e[g];delete b[a]})}function n(b,c){a.each(c,function(a){var d=b[a],e=o(b,c[a].chunkNames),f=p(e,c[a].chunkNames);d=q(c[a].formatString,f),b[a]=j(d)})}function o(a,b){for(var c,d={},e=b.length,f=0;e>f;f++)c=b[f],d[c]=a[c],delete a[c];return d}function p(a,b){C.length=0;for(var c=b.length,d=0;c>d;d++)C.push(a[b[d]]);return C}function q(a,b){for(var c=a,d=b.length,e=0;d>e;e++)c=c.replace(A,+b[e].toFixed(4));return c}function r(a){return a.match(w)}function s(b,c){a.each(c,function(a){var d,e=c[a],f=e.chunkNames,g=f.length,h=b[a];if("string"==typeof h){var i=h.split(" "),j=i[i.length-1];for(d=0;g>d;d++)b[f[d]]=i[d]||j}else for(d=0;g>d;d++)b[f[d]]=h;delete b[a]})}function t(b,c){a.each(c,function(a){var d=c[a],e=d.chunkNames,f=e.length,g=b[e[0]],h=typeof g;if("string"===h){for(var i="",j=0;f>j;j++)i+=" "+b[e[j]],delete b[e[j]];b[a]=i.substr(1)}else b[a]=g})}var u=/(\d|\-|\.)/,v=/([^\-0-9\.]+)/g,w=/[0-9.\-]+/g,x=new RegExp("rgb\\("+w.source+/,\s*/.source+w.source+/,\s*/.source+w.source+"\\)","g"),y=/^.*\(/,z=/#([0-9]|[a-f]){3,6}/gi,A="VAL",B=[],C=[];a.prototype.filter.token={tweenCreated:function(a,b,c,e){d(a),d(b),d(c),this._tokenData=l(a)},beforeTween:function(a,b,c,d){s(d,this._tokenData),m(a,this._tokenData),m(b,this._tokenData),m(c,this._tokenData)},afterTween:function(a,b,c,d){n(a,this._tokenData),n(b,this._tokenData),n(c,this._tokenData),t(d,this._tokenData)}}}(e)}).call(null)},{}],2:[function(a,b,c){var d=a("./shape"),e=a("./utils"),f=function(a,b){this._pathTemplate="M 50,50 m 0,-{radius} a {radius},{radius} 0 1 1 0,{2radius} a {radius},{radius} 0 1 1 0,-{2radius}",this.containerAspectRatio=1,d.apply(this,arguments)};f.prototype=new d,f.prototype.constructor=f,f.prototype._pathString=function(a){var b=a.strokeWidth;a.trailWidth&&a.trailWidth>a.strokeWidth&&(b=a.trailWidth);var c=50-b/2;return e.render(this._pathTemplate,{radius:c,"2radius":2*c})},f.prototype._trailString=function(a){return this._pathString(a)},b.exports=f},{"./shape":7,"./utils":8}],3:[function(a,b,c){var d=a("./shape"),e=a("./utils"),f=function(a,b){this._pathTemplate="M 0,{center} L 100,{center}",d.apply(this,arguments)};f.prototype=new d,f.prototype.constructor=f,f.prototype._initializeSvg=function(a,b){a.setAttribute("viewBox","0 0 100 "+b.strokeWidth),a.setAttribute("preserveAspectRatio","none")},f.prototype._pathString=function(a){return e.render(this._pathTemplate,{center:a.strokeWidth/2})},f.prototype._trailString=function(a){return this._pathString(a)},b.exports=f},{"./shape":7,"./utils":8}],4:[function(a,b,c){b.exports={Line:a("./line"),Circle:a("./circle"),SemiCircle:a("./semicircle"),Path:a("./path"),Shape:a("./shape"),utils:a("./utils")}},{"./circle":2,"./line":3,"./path":5,"./semicircle":6,"./shape":7,"./utils":8}],5:[function(a,b,c){var d=a("shifty"),e=a("./utils"),f={easeIn:"easeInCubic",easeOut:"easeOutCubic",easeInOut:"easeInOutCubic"},g=function h(a,b){if(!(this instanceof h))throw new Error("Constructor was called without new keyword");b=e.extend({duration:800,easing:"linear",from:{},to:{},step:function(){}},b);var c;c=e.isString(a)?document.querySelector(a):a,this.path=c,this._opts=b,this._tweenable=null;var d=this.path.getTotalLength();this.path.style.strokeDasharray=d+" "+d,this.set(0)};g.prototype.value=function(){var a=this._getComputedDashOffset(),b=this.path.getTotalLength(),c=1-a/b;return parseFloat(c.toFixed(6),10)},g.prototype.set=function(a){this.stop(),this.path.style.strokeDashoffset=this._progressToOffset(a);var b=this._opts.step;if(e.isFunction(b)){var c=this._easing(this._opts.easing),d=this._calculateTo(a,c),f=this._opts.shape||this;b(d,f,this._opts.attachment)}},g.prototype.stop=function(){this._stopTween(),this.path.style.strokeDashoffset=this._getComputedDashOffset()},g.prototype.animate=function(a,b,c){b=b||{},e.isFunction(b)&&(c=b,b={});var f=e.extend({},b),g=e.extend({},this._opts);b=e.extend(g,b);var h=this._easing(b.easing),i=this._resolveFromAndTo(a,h,f);this.stop(),this.path.getBoundingClientRect();var j=this._getComputedDashOffset(),k=this._progressToOffset(a),l=this;this._tweenable=new d,this._tweenable.tween({from:e.extend({offset:j},i.from),to:e.extend({offset:k},i.to),duration:b.duration,easing:h,step:function(a){l.path.style.strokeDashoffset=a.offset;var c=b.shape||l;b.step(a,c,b.attachment)},finish:function(a){e.isFunction(c)&&c()}})},g.prototype._getComputedDashOffset=function(){var a=window.getComputedStyle(this.path,null);return parseFloat(a.getPropertyValue("stroke-dashoffset"),10)},g.prototype._progressToOffset=function(a){var b=this.path.getTotalLength();return b-a*b},g.prototype._resolveFromAndTo=function(a,b,c){return c.from&&c.to?{from:c.from,to:c.to}:{from:this._calculateFrom(b),to:this._calculateTo(a,b)}},g.prototype._calculateFrom=function(a){return d.interpolate(this._opts.from,this._opts.to,this.value(),a)},g.prototype._calculateTo=function(a,b){return d.interpolate(this._opts.from,this._opts.to,a,b)},g.prototype._stopTween=function(){null!==this._tweenable&&(this._tweenable.stop(),this._tweenable=null)},g.prototype._easing=function(a){return f.hasOwnProperty(a)?f[a]:a},b.exports=g},{"./utils":8,shifty:1}],6:[function(a,b,c){var d=a("./shape"),e=a("./circle"),f=a("./utils"),g=function(a,b){this._pathTemplate="M 50,50 m -{radius},0 a {radius},{radius} 0 1 1 {2radius},0",this.containerAspectRatio=2,d.apply(this,arguments)};g.prototype=new d,g.prototype.constructor=g,g.prototype._initializeSvg=function(a,b){a.setAttribute("viewBox","0 0 100 50")},g.prototype._initializeTextContainer=function(a,b,c){a.text.style&&(c.style.top="auto",c.style.bottom="0",a.text.alignToBottom?f.setStyle(c,"transform","translate(-50%, 0)"):f.setStyle(c,"transform","translate(-50%, 50%)"))},g.prototype._pathString=e.prototype._pathString,g.prototype._trailString=e.prototype._trailString,b.exports=g},{"./circle":2,"./shape":7,"./utils":8}],7:[function(a,b,c){var d=a("./path"),e=a("./utils"),f="Object is destroyed",g=function h(a,b){if(!(this instanceof h))throw new Error("Constructor was called without new keyword");if(0!==arguments.length){this._opts=e.extend({color:"#555",strokeWidth:1,trailColor:null,trailWidth:null,fill:null,text:{style:{color:null,position:"absolute",left:"50%",top:"50%",padding:0,margin:0,transform:{prefix:!0,value:"translate(-50%, -50%)"}},autoStyleContainer:!0,alignToBottom:!0,value:null,className:"progressbar-text"},svgStyle:{display:"block",width:"100%"},warnings:!1},b,!0),e.isObject(b)&&void 0!==b.svgStyle&&(this._opts.svgStyle=b.svgStyle),e.isObject(b)&&e.isObject(b.text)&&void 0!==b.text.style&&(this._opts.text.style=b.text.style);var c,f=this._createSvgView(this._opts);if(c=e.isString(a)?document.querySelector(a):a,!c)throw new Error("Container does not exist: "+a);this._container=c,this._container.appendChild(f.svg),this._opts.warnings&&this._warnContainerAspectRatio(this._container),this._opts.svgStyle&&e.setStyles(f.svg,this._opts.svgStyle),this.svg=f.svg,this.path=f.path,this.trail=f.trail,this.text=null;var g=e.extend({attachment:void 0,shape:this},this._opts);this._progressPath=new d(f.path,g),e.isObject(this._opts.text)&&null!==this._opts.text.value&&this.setText(this._opts.text.value)}};g.prototype.animate=function(a,b,c){if(null===this._progressPath)throw new Error(f);this._progressPath.animate(a,b,c)},g.prototype.stop=function(){if(null===this._progressPath)throw new Error(f);void 0!==this._progressPath&&this._progressPath.stop()},g.prototype.destroy=function(){if(null===this._progressPath)throw new Error(f);this.stop(),this.svg.parentNode.removeChild(this.svg),this.svg=null,this.path=null,this.trail=null,this._progressPath=null,null!==this.text&&(this.text.parentNode.removeChild(this.text),this.text=null)},g.prototype.set=function(a){if(null===this._progressPath)throw new Error(f);this._progressPath.set(a)},g.prototype.value=function(){if(null===this._progressPath)throw new Error(f);return void 0===this._progressPath?0:this._progressPath.value()},g.prototype.setText=function(a){if(null===this._progressPath)throw new Error(f);null===this.text&&(this.text=this._createTextContainer(this._opts,this._container),this._container.appendChild(this.text)),e.isObject(a)?(e.removeChildren(this.text),this.text.appendChild(a)):this.text.innerHTML=a},g.prototype._createSvgView=function(a){var b=document.createElementNS("http://www.w3.org/2000/svg","svg");this._initializeSvg(b,a);var c=null;(a.trailColor||a.trailWidth)&&(c=this._createTrail(a),b.appendChild(c));var d=this._createPath(a);return b.appendChild(d),{svg:b,path:d,trail:c}},g.prototype._initializeSvg=function(a,b){a.setAttribute("viewBox","0 0 100 100")},g.prototype._createPath=function(a){var b=this._pathString(a);return this._createPathElement(b,a)},g.prototype._createTrail=function(a){var b=this._trailString(a),c=e.extend({},a);return c.trailColor||(c.trailColor="#eee"),c.trailWidth||(c.trailWidth=c.strokeWidth),c.color=c.trailColor,c.strokeWidth=c.trailWidth,c.fill=null,this._createPathElement(b,c)},g.prototype._createPathElement=function(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg","path");return c.setAttribute("d",a),c.setAttribute("stroke",b.color),c.setAttribute("stroke-width",b.strokeWidth),b.fill?c.setAttribute("fill",b.fill):c.setAttribute("fill-opacity","0"),c},g.prototype._createTextContainer=function(a,b){var c=document.createElement("div");c.className=a.text.className;var d=a.text.style;return d&&(a.text.autoStyleContainer&&(b.style.position="relative"),e.setStyles(c,d),d.color||(c.style.color=a.color)),this._initializeTextContainer(a,b,c),c},g.prototype._initializeTextContainer=function(a,b,c){},g.prototype._pathString=function(a){throw new Error("Override this function for each progress bar")},g.prototype._trailString=function(a){throw new Error("Override this function for each progress bar")},g.prototype._warnContainerAspectRatio=function(a){if(this.containerAspectRatio){var b=window.getComputedStyle(a,null),c=parseFloat(b.getPropertyValue("width"),10),d=parseFloat(b.getPropertyValue("height"),10);e.floatEquals(this.containerAspectRatio,c/d)||(console.warn("Incorrect aspect ratio of container","#"+a.id,"detected:",b.getPropertyValue("width")+"(width)","/",b.getPropertyValue("height")+"(height)","=",c/d),console.warn("Aspect ratio of should be",this.containerAspectRatio))}},b.exports=g},{"./path":5,"./utils":8}],8:[function(a,b,c){function d(a,b,c){a=a||{},b=b||{},c=c||!1;for(var e in b)if(b.hasOwnProperty(e)){var f=a[e],g=b[e];c&&l(f)&&l(g)?a[e]=d(f,g,c):a[e]=g}return a}function e(a,b){var c=a;for(var d in b)if(b.hasOwnProperty(d)){var e=b[d],f="\\{"+d+"\\}",g=new RegExp(f,"g");c=c.replace(g,e)}return c}function f(a,b,c){for(var d=a.style,e=0;e