Compare commits

..

14 Commits

Author SHA1 Message Date
Kasper Rynning-Tønnesen
0501a7ebbb Chat 2019-11-17 00:10:58 +01:00
Kasper Rynning-Tønnesen
4aa216947a playlist 2019-11-16 23:04:35 +01:00
Kasper Rynning-Tønnesen
53a9b83f6b Merge branch 'vue-setup' of github.com:KevinMidboe/zoff into vue-setup 2019-11-16 22:13:26 +01:00
Kasper Rynning-Tønnesen
a06f965163 Playlist-element 2019-11-16 21:32:43 +01:00
f90cf2132b NO longer opens a new window and changed port 8000 -> 8080. 2019-11-16 21:18:13 +01:00
3680d9ba43 Started a store 2019-11-16 21:04:00 +01:00
cb0d3633ac Imported normalize styles into a un-scoped style section of App.vue 2019-11-16 21:03:48 +01:00
178f21e6c8 Working youtube iframe player 2019-11-16 21:02:59 +01:00
6b40947302 Router defined. Added player component to router 2019-11-16 19:39:15 +01:00
6caef24fd8 Commented out router declaration 2019-11-16 17:21:18 +01:00
a604e9de8f Removed unused component body 2019-11-16 17:21:04 +01:00
1e8770a226 NOT COMPILING. Init vue setup. 2019-11-16 17:20:13 +01:00
b32710b4d3 Updated package dependencies 2019-11-16 17:19:37 +01:00
Kasper Rynning-Tønnesen
ce7ac96a5d Fix for casting-crashing in other browsers without casting 2019-11-15 16:58:58 +01:00
26 changed files with 8926 additions and 46 deletions

3
config/env/dev.env.js vendored Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
NODE_ENV: "development"
};

3
config/env/prod.env.js vendored Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
NODE_ENV: "production"
};

3
config/env/staging.env.js vendored Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
NODE_ENV: "staging"
};

15
config/helpers.js Normal file
View File

@@ -0,0 +1,15 @@
"use strict";
const path = require("path");
const _root = path.resolve(__dirname, "..");
exports.root = function(args) {
args = Array.prototype.slice.call(arguments, 0);
return path.join.apply(path, [_root].concat(args));
};
exports.assetsPath = function(_path) {
return path.posix.join("static", _path);
};

View File

@@ -0,0 +1,61 @@
"use strict";
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const MiniCSSExtractPlugin = require("mini-css-extract-plugin");
const helpers = require("./helpers");
const isDev = process.env.NODE_ENV === "development";
const webpackConfig = function(isDev) {
return {
entry: {
main: ["@babel/polyfill", helpers.root("frontend", "main")]
},
resolve: {
extensions: [".js", ".vue"],
alias: {
vue$: isDev ? "vue/dist/vue.runtime.js" : "vue/dist/vue.runtime.min.js",
"@": helpers.root("frontend")
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
include: [helpers.root("frontend")]
},
{
test: /\.js$/,
loader: "babel-loader",
include: [helpers.root("frontend")]
},
{
test: /\.css$/,
use: [
isDev ? "vue-style-loader" : MiniCSSExtractPlugin.loader,
{ loader: "css-loader", options: { sourceMap: isDev } }
]
},
{
test: /\.scss$/,
use: [
isDev ? "vue-style-loader" : MiniCSSExtractPlugin.loader,
{ loader: "css-loader", options: { sourceMap: isDev } },
{ loader: "sass-loader", options: { sourceMap: isDev } }
]
},
{
test: /\.sass$/,
use: [
isDev ? "vue-style-loader" : MiniCSSExtractPlugin.loader,
{ loader: "css-loader", options: { sourceMap: isDev } },
{ loader: "sass-loader", options: { sourceMap: isDev } }
]
}
]
},
plugins: [new VueLoaderPlugin()]
};
};
module.exports = webpackConfig;

View File

@@ -0,0 +1,44 @@
"use strict";
const webpack = require("webpack");
const merge = require("webpack-merge");
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
const HtmlPlugin = require("html-webpack-plugin");
const helpers = require("./helpers");
const commonConfig = require("./webpack.config.common");
const environment = require("./env/dev.env");
const webpackConfig = merge(commonConfig(true), {
mode: "development",
devtool: "cheap-module-eval-source-map",
output: {
path: helpers.root("dist"),
publicPath: "/",
filename: "js/[name].bundle.js",
chunkFilename: "js/[id].chunk.js"
},
optimization: {
runtimeChunk: "single",
splitChunks: {
chunks: "all"
}
},
plugins: [
new webpack.EnvironmentPlugin(environment),
new webpack.HotModuleReplacementPlugin(),
new FriendlyErrorsPlugin(),
new HtmlPlugin({ template: "frontend/index.html", chunksSortMode: "dependency" })
],
devServer: {
compress: true,
historyApiFallback: true,
hot: true,
overlay: true,
port: 8080,
stats: {
normal: true
}
}
});
module.exports = webpackConfig;

View File

@@ -0,0 +1,55 @@
"use strict";
const webpack = require("webpack");
const merge = require("webpack-merge");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const MiniCSSExtractPlugin = require("mini-css-extract-plugin");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const helpers = require("./helpers");
const commonConfig = require("./webpack.config.common");
const isProd = process.env.NODE_ENV === "production";
const environment = isProd
? require("./env/prod.env")
: require("./env/staging.env");
const webpackConfig = merge(commonConfig(false), {
mode: "production",
output: {
path: helpers.root("dist"),
publicPath: "/",
filename: "js/[name].bundle.js"
},
optimization: {
minimizer: [
new OptimizeCSSAssetsPlugin({
cssProcessorPluginOptions: {
preset: ["default", { discardComments: { removeAll: true } }]
}
}),
new UglifyJSPlugin({
cache: true,
parallel: false,
sourceMap: !isProd
})
]
},
plugins: [
new webpack.EnvironmentPlugin(environment),
new MiniCSSExtractPlugin({
filename: "css/[name].css"
})
]
});
if (!isProd) {
webpackConfig.devtool = "source-map";
if (process.env.npm_config_report) {
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}
}
module.exports = webpackConfig;

9
frontend/App.vue Normal file
View File

@@ -0,0 +1,9 @@
<template>
<div>
<router-view />
</div>
</template>
<style lang="scss">
@import './styles/normalize';
</style>

View File

@@ -0,0 +1,93 @@
<template>
<div class="conatiner">
<h1>Player</h1>
<div id="player"></div>
</div>
</template>
<script>
export default {
data() {
return {
done: false,
player: undefined
}
},
beforeMount() {
this.loadYoutubeIframe()
},
watch: {
done: function(val) {
console.log('done changed to:', val)
if (val === true) {
this.player.playVideo();
}
}
},
methods: {
loadYoutubeIframe() {
// const tag = this.$refs.player;
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
const firstScriptTag = document.getElementsByTagName('script')[0]
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
const that = this;
// setTimeout(() => that.onYouTubeIframeAPIReady(), 2000);
window.onYouTubeIframeAPIReady = this.onYouTubeIframeAPIReady
},
onYouTubeIframeAPIReady() {
console.log('we are loaded and ready to fucking go!')
this.player = new YT.Player('player', {
videoId: 'SJOgTMP8cs4',
playerVars: {
rel: "0",
autoplay: 1,
wmode: "transparent",
controls: "0",
fs: "0",
iv_load_policy: "3",
theme: "light",
color: "white",
showinfo: 0
},
events: {
onReady: this.onPlayerReady,
onStateChange: this.onPlayerStateChange,
onError: this.errorHandler
}
});
},
onPlayerReady(event) {
console.log('event from onPlayerReady', event)
event.target.playVideo();
},
onPlayerStateChange(event) {
console.log('on player changed')
if (event.data === YT.PlayerState.PLAYING && !this.done) {
this.done = true
}
},
errorHandler(error) {
console.log('error handling youtube player. Error:', error)
}
}
}
</script>
<style lang="scss">
#player {
width: 100vw !important;
height: 60vh;
}
</style>
<style lang="scss" scoped>
.container {
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,30 @@
<template>
<div>
<h1>Styleguide</h1>
<div class="row">
<h2>Chat</h2>
<Chat />
</div>
<div class="row">
<h2>Playlist-element</h2>
<Playlist />
</div>
</div>
</template>
<script>
import Playlist from "@/components/playlist/Playlist";
import Chat from "@/components/chat/Chat";
export default {
components: {
Chat,
Playlist
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,149 @@
<template>
<div class="chat-container">
<ul>
<li v-for="(message, i) in chat" :key="i">
<Name :name="message.name" :icon="message.icon" />
<span class="channel-name" v-if="message.channel != null">{{ message.channel }}</span>
<span class="channel-message">: {{ message.message }}</span>
</li>
</ul>
<div class="chat-input">
<ChatInput @sentMessage="sentMessage" @requestHelp="requestHelp" />
</div>
</div>
</template>
<script>
import Name from "@/components/chat/Name";
import ChatInput from "@/components/chat/ChatInput";
export default {
components: {
Name,
ChatInput
},
watch: {
chat: function(_chat) {
if (_chat.length > 30) {
this.chat = _chat
.reverse()
.splice(0, 30)
.reverse();
}
}
},
methods: {
sentMessage: function(message) {
this.chat.push({
name: "pepega",
icon: null,
message: message,
channel: "avicii"
});
},
requestHelp: function() {
}
},
data() {
return {
chat: [
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
},
{
name: "KasperRT",
icon: "https://img.youtube.com/vi/fn_amMJehPU/mqdefault.jpg",
message: "Hei på deg",
channel: "summér"
}
]
};
}
};
</script>
<style scoped lang="scss">
ul {
list-style: none;
padding: 0;
& li {
display: flex;
color: white;
& .channel-name {
font-size: 0.75rem;
display: flex;
justify-content: center;
align-items: center;
color: whitesmoke;
}
&:nth-child(even) {
background: #00000040;
}
&:nth-child(odd) {
background: #00000050;
}
}
}
</style>

View File

@@ -0,0 +1,33 @@
<template>
<div class="chat-input">
<input v-model="chatInput" type="text" placeholder="Aa.." @keyup.enter="send" />
<button @click="send">Send</button>
<button @click="requestHelp">Help</button>
</div>
</template>
<script>
export default {
data() {
return {
chatInput: ""
}
},
methods: {
send: function() {
if(this.chatInput == "") {
return;
}
this.$emit("sentMessage", this.chatInput);
this.chatInput = "";
},
requestHelp: function() {
console.log("emit for help here");
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,41 @@
<template>
<div class="name">
<div class="icon-container" v-if="icon != undefined">
<img :src="icon" />
</div>
<div class="name-container">{{ name }}</div>
</div>
</template>
<script>
export default {
props: {
name: {
type: String,
required: true
},
icon: {
type: String,
required: false
}
}
};
</script>
<style scoped lang="scss">
.name {
display: flex;
align-items: center;
img {
height: 10px;
width: 10px;
padding: 0 5px;
}
.name-container {
padding: 0 5px 0 5px;
color: orange;
}
}
</style>

View File

@@ -0,0 +1,113 @@
<template>
<div class="context-menu-container" :style="'top:' + mouseY + 'px;left:' + mouseX + 'px;'">
<ul>
<li @click="copyLink">Copy link</li>
<li @click="findSimilar">Find similar</li>
<li class="addedBy">Added by {{ addedBy }}</li>
<hr />
<li @click="deleteSong" :disabled="loggedIn">Delete</li>
</ul>
<div class="context-menu-background" @mouseout="closeSelf" @click="closeSelf"></div>
</div>
</template>
<script>
export default {
props: {
id: {
required: true,
type: String
},
type: {
required: true,
type: String
},
addedBy: {
required: true,
type: String
},
mouseX: {
required: true,
type: Number
},
mouseY: {
required: true,
type: Number
}
},
computed: {
loggedIn: function() {
return false;
}
},
methods: {
closeSelf: function(e) {
if (e.toElement == null || e.toElement.nodeName == null) {
return;
}
if (e.toElement.nodeName == "UL" || e.toElement.nodeName == "LI") {
return;
}
this.$emit("closeContextMenu");
},
copyLink: function() {
console.log("copy link of", this.id);
},
findSimilar: function() {
console.log("find similar of", this.id);
},
deleteSong: function() {
console.log("delete song of", this.id);
}
}
};
</script>
<style scoped lang="scss">
.context-menu-background {
background: #000000a0;
width: 500px;
height: 500px;
position: absolute;
left: -85%;
top: -100%;
z-index: 1;
}
.context-menu-container {
font-family: sans-serif;
position: absolute;
background-color: #2d2d2d;
border-radius: 10px;
color: white;
& ul {
z-index: 2;
list-style: none;
padding: 10px 0 10px 0;
margin: 0;
position: relative;
background-color: #2d2d2d;
border-radius: 10px;
color: white;
& li {
cursor: pointer;
padding: 5px 15px;
&:hover:not(.addedBy) {
background-color: #ffffff15;
}
&.addedBy {
cursor: initial;
}
}
& hr {
padding: 0px;
margin: 0;
}
}
}
</style>

View File

@@ -0,0 +1,181 @@
<template>
<div class="playlist-conatiner">
<div class="playlist-element" v-for="(song, i) in paginatedList" :key="i">
<Song
class="song"
:title="song.title"
:thumbnail="song.thumbnail"
:votes="song.votes"
:addedBy="song.added_by"
:id="song.id"
:type="song.type"
:duration="song.duration"
@contextmenu="moreInfo"
/>
<div class="song-context-button" @click="moreInfo($event, song.id)">. . .</div>
</div>
<div class="pagination-buttons">
<button @click="firstPage" :disabled="disabledPrev" class="first"><</button>
<button @click="prevPage" :disabled="disabledPrev">previous</button>
<span>{{ page + 1 }} / {{ pages }}</span>
<button @click="nextPage" :disabled="disabledNext">next</button>
<button @click="lastPage" :disabled="disabledNext" class="last">></button>
</div>
<ContextMenu
v-if="contextMenuOpen"
:addedBy="contextOnElement.added_by"
:id="contextOnElement.id"
:type="contextOnElement.type"
:mouseX="mouseX"
:mouseY="mouseY"
@closeContextMenu="closeContextMenu"
/>
</div>
</template>
<script>
import Song from "@/components/playlist/Song";
import ContextMenu from "@/components/playlist/ContextMenu";
export default {
components: {
Song,
ContextMenu
},
computed: {
paginatedList: function() {
return this.playlist.slice(
this.page * this.perPage,
(1 + this.page) * this.perPage
);
},
disabledPrev: function() {
return this.page == 0;
},
disabledNext: function() {
return this.playlist.length < (this.page + 1) * this.perPage;
},
pages: function() {
return Math.ceil(this.playlist.length / this.perPage);
}
},
async beforeMount() {
try {
const request = await fetch("https://zoff.me/api/list/summér", {
method: "POST"
});
const playlist = await request.json();
if (this.playlist.error == true) {
console.error(this.playlist.error);
return;
}
this.playlist = playlist.results;
} catch (e) {
alert("TIMEOUT");
}
},
methods: {
moreInfo: function(e, id) {
e.preventDefault();
this.contextOnElement = this.playlist.find(song => song.id == id);
this.mouseX = e.pageX;
this.mouseY = e.pageY;
this.contextMenuOpen = true;
},
closeContextMenu: function() {
this.contextMenuOpen = false;
this.contextOnElement = null;
},
firstPage: function() {
this.page = 0;
},
lastPage: function() {
this.page = Math.floor(this.playlist.length / this.perPage);
},
prevPage: function() {
if (this.page == 0) {
return;
}
this.page -= 1;
},
nextPage: function() {
if (this.playlist.length < (this.page + 1) * this.perPage) {
return;
}
this.page += 1;
}
},
data() {
return {
contextMenuOpen: false,
contextOnElement: {},
page: 0,
perPage: 10,
playlist: []
};
}
};
</script>
<style scoped lang="scss">
.playlist-conatiner {
background-color: #2d2d2d;
padding-top: 5px;
& .playlist-element {
display: flex;
flex-direction: row;
box-shadow: 0px 0px 2px #000000;
border-radius: 5px;
background: #2d2d2d;
color: white;
margin: 5px 5px;
cursor: pointer;
&:hover {
box-shadow: 0px 0px 3px #000000;
}
&:active {
background: #000000;
}
& .song {
width: 90%;
}
& .song-context-button {
width: 10%;
display: flex;
justify-content: center;
align-items: center;
border-left: 1px solid black;
}
}
.pagination-buttons {
display: flex;
justify-content: space-between;
color: white;
padding: 10px 0;
& span {
display: flex;
justify-content: center;
align-items: center;
}
& button {
width: 35%;
height: 30px;
background: transparent;
color: white;
border: none;
&.first,
&.last {
width: 10%;
}
}
}
}
</style>

View File

@@ -0,0 +1,136 @@
<template>
<div class="song-container">
<div
class="song-voteable-container"
@click="clickedSong"
@contextmenu="$emit('contextmenu', $event, id)"
>
<div class="song-thumbnail">
<img :src="thumbnail" :alt="title" />
<span class="song-duration">{{ durationInString }}</span>
</div>
<div class="song-info">
<div class="song-title">{{ title }}</div>
<div class="song-votes">{{ votes }} vote{{votes > 1 || votes == 0 ? "s" : null }}</div>
</div>
</div>
</div>
</template>
<script>
import ContextMenu from "@/components/playlist/ContextMenu";
export default {
components: {
ContextMenu
},
props: {
id: {
required: true,
type: String
},
thumbnail: {
required: true,
type: String
},
title: {
required: true,
type: String
},
votes: {
required: true,
type: Number
},
addedBy: {
required: true,
type: String
},
type: {
required: true,
type: String
},
duration: {
required: true,
type: Number
}
},
data() {
return {
contextMenuOpen: false,
mouseX: 0,
mouseY: 0
};
},
computed: {
durationInString: function() {
let time = this.duration;
let minutes = Math.floor(time / 60);
time = time - minutes * 60;
let minutesString = (minutes + "").padStart(2, "0");
let secondString = (time + "").padStart(2, "0");
return `${minutesString}:${secondString}`;
}
},
methods: {
clickedSong: function() {
console.log("Clicked on song with info", this.title, this.id);
}
}
};
</script>
<style scoped lang="scss">
.song-container {
display: flex;
flex-direction: row;
color: white;
.song-voteable-container {
width: 75%;
display: flex;
& .song-votes {
color: lightgrey;
}
& .song-thumbnail {
width: 25%;
position: relative;
& img {
width: 100%;
height: 100%;
border-top-left-radius: 7.5px;
border-bottom-left-radius: 7.5px;
}
& .song-duration {
position: absolute;
bottom: 5px;
left: 0px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-left-radius: 5px;
padding: 0 5px;
background: #000000A0;
color:white;
}
}
& .song-info {
width: 100%;
padding-left: 10px;
display: flex;
justify-content: space-evenly;
flex-direction: column;
}
}
& .song-mutation {
width: 25%;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

9
frontend/index.html Normal file
View File

@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Zoff</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

16
frontend/main.js Normal file
View File

@@ -0,0 +1,16 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import router from './routes'
import store from './store'
import App from './App.vue'
Vue.use(VueRouter)
new Vue({
el: '#app',
router,
store,
components: { App },
render: h => h(App)
})

View File

@@ -0,0 +1,21 @@
export default {
namespaced: true,
state: {
player: undefined
},
getters: {
player: (state) => {
return state.player;
}
},
mutations: {
SET_PLAYER: (state, player) => {
state.player = player;
}
},
actios: {
setPlayer({ commit }, player) {
commit('SET_PLAYER', player)
}
}
}

31
frontend/routes.js Normal file
View File

@@ -0,0 +1,31 @@
import Vue from 'vue'
import VueRouter from 'vue-router';
Vue.use(VueRouter)
let routes = [
{
name: 'player',
path: '/player',
component: (resolve) => require(['./components/Player.vue'], resolve)
},
{
name: 'styleguide',
path: '/styleguide',
component: (resolve) => require(['./components/Styleguide.vue'], resolve)
},
// {
// name: '404',
// path: '/404',
// component: (resolve) => require(['./components/404.vue'], resolve)
// }
];
const router = new VueRouter({
mode: 'hash',
base: '/',
routes,
linkActiveClass: 'is-active'
});
export default router;

12
frontend/store.js Normal file
View File

@@ -0,0 +1,12 @@
import Vue from 'vue';
import Vuex from 'vuex';
import playerModule from '@/modules/playerModule';
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
playerModule
}
})

6
frontend/styles/normalize.scss vendored Normal file
View File

@@ -0,0 +1,6 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}

7853
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@
"main": "server/app.js",
"scripts": {
"start": "npm install --only=dev && npm install && $(npm bin)/gulp build && node server/app.js",
"vue": "cross-env NODE_ENV=development webpack-dev-server --progress",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
@@ -20,12 +21,40 @@
"url": "https://github.com/zoff-music/zoff/issues"
},
"devDependencies": {
"cross-env": "~5.2",
"gulp": "^4.0.0",
"gulp-concat": "^2.6.1",
"gulp-uglify": "^3.0.2"
"gulp-uglify": "^3.0.2",
"@babel/core": "~7.2",
"@babel/plugin-proposal-class-properties": "~7.3",
"@babel/plugin-proposal-decorators": "~7.3",
"@babel/plugin-proposal-json-strings": "~7.2",
"@babel/plugin-syntax-dynamic-import": "~7.2",
"@babel/plugin-syntax-import-meta": "~7.2",
"@babel/preset-env": "~7.3",
"babel-loader": "~8.0",
"compression-webpack-plugin": "~2.0",
"css-loader": "~0.28",
"friendly-errors-webpack-plugin": "~1.7",
"html-webpack-plugin": "~3.2",
"mini-css-extract-plugin": "~0.5",
"node-sass": "~4.11",
"optimize-css-assets-webpack-plugin": "~3.2",
"sass-loader": "~7.1",
"uglifyjs-webpack-plugin": "~1.2",
"vue-loader": "~15.6",
"vue-style-loader": "~4.1",
"vue-template-compiler": "~2.6",
"webpack": "~4.29",
"webpack-bundle-analyzer": "~3.0",
"webpack-cli": "~3.2",
"webpack-dev-server": "~3.1",
"webpack-hot-middleware": "~2.24",
"webpack-merge": "~4.2"
},
"homepage": "https://github.com/zoff-music/zoff#readme",
"dependencies": {
"@babel/polyfill": "~7.2",
"bad-words": "^1.6.5",
"bcrypt-nodejs": "0.0.3",
"body-parser": "^1.18.3",
@@ -38,6 +67,7 @@
"express-handlebars": "^3.0.2",
"express-recaptcha": "^3.0.1",
"express-session": "^1.15.6",
"extract-text-webpack-plugin": "^3.0.2",
"farmhash": "^3.0.0",
"feature-policy": "^0.2.0",
"gulp-clean-css": "^4.2.0",
@@ -48,7 +78,7 @@
"mongodb": "^2.2.36",
"mongojs": "^2.6.0",
"mongojs-paginate": "^1.2.0",
"mongoose": "^5.7.5",
"mongoose": "^5.4.18",
"mpromise": "^0.5.5",
"nodemailer": "^4.7.0",
"passport": "^0.4.0",
@@ -59,6 +89,9 @@
"socket.io": "^2.2.0",
"socket.io-redis": "^5.2.0",
"sticky-session": "^1.1.2",
"uniqid": "5.0.3"
"uniqid": "5.0.3",
"vue": "~2.6",
"vue-router": "~3.0",
"vuex": "^3.1.2"
}
}

View File

@@ -350,7 +350,12 @@ temp_pass = "";
initializeCastApi = function() {
try {
if (cast == undefined) return;
if (
cast == undefined ||
chrome.cast == undefined ||
chrome.cast.AutoJoinPolicy == undefined
)
return;
} catch (event) {
return;
}

9
webpack.config.js Normal file
View File

@@ -0,0 +1,9 @@
"use strict";
const environment = (process.env.NODE_ENV || "development").trim();
if (environment === "development") {
module.exports = require("./config/webpack.config.dev");
} else {
module.exports = require("./config/webpack.config.prod");
}