entire application
This commit is contained in:
parent
c6eecdefd6
commit
f4d7506f7d
23 changed files with 897 additions and 2 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
node_modules/*
|
||||||
|
vendor/*
|
||||||
|
public/app.*
|
||||||
|
config.ini
|
||||||
|
[Tt]humbs.db
|
||||||
|
desktop.ini
|
||||||
|
$RECYCLE.BIN/
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2016 Julian van de Groep
|
Copyright (c) 2016 Julian van de Groep <https://flash.moe>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
# now-listening
|
# now listening
|
||||||
A replacement for the old /now page on lastfm profiles
|
A replacement for the old /now page on lastfm profiles
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
https://now.flash.moe/#/{last.fm username}
|
||||||
|
|
5
composer.json
Normal file
5
composer.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"matto1990/lastfm-api": "^1.2"
|
||||||
|
}
|
||||||
|
}
|
70
composer.lock
generated
Normal file
70
composer.lock
generated
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"hash": "0447259d6060720f3c189f0fdca5c123",
|
||||||
|
"content-hash": "94d9635fa82a3525da82ed39e8471a8b",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "matto1990/lastfm-api",
|
||||||
|
"version": "v1.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/matto1990/PHP-Last.fm-API.git",
|
||||||
|
"reference": "1cf9a8947bf756beb876d5f8e2af398058805e08"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/matto1990/PHP-Last.fm-API/zipball/1cf9a8947bf756beb876d5f8e2af398058805e08",
|
||||||
|
"reference": "1cf9a8947bf756beb876d5f8e2af398058805e08",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php": ">=5.3.3",
|
||||||
|
"phpunit/phpunit": "~4.8"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"LastFmApi\\": "src/lastfmapi/",
|
||||||
|
"Tests\\": "tests"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Marcos",
|
||||||
|
"email": "devilcius@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Matt",
|
||||||
|
"email": "matt@oakes.ws"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Last.fm webservice client",
|
||||||
|
"homepage": "https://github.com/matto1990/PHP-Last.fm-API",
|
||||||
|
"keywords": [
|
||||||
|
"api",
|
||||||
|
"last.fm",
|
||||||
|
"webservice client"
|
||||||
|
],
|
||||||
|
"time": "2016-03-17 16:41:24"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": []
|
||||||
|
}
|
2
config.example.ini
Normal file
2
config.example.ini
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
api_key = your api key here
|
||||||
|
endpoint = https://ws.audioscrobbler.com/2.0/
|
53
gulpfile.js
Normal file
53
gulpfile.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// aliases
|
||||||
|
var gulp = require('gulp'),
|
||||||
|
less = require('gulp-less'),
|
||||||
|
ts = require('gulp-typescript'),
|
||||||
|
concat = require('gulp-concat'),
|
||||||
|
jsmin = require('gulp-minify'),
|
||||||
|
path = require('path');
|
||||||
|
|
||||||
|
// variables
|
||||||
|
var destination = './public',
|
||||||
|
less_sources = './src/less/**/*.less',
|
||||||
|
less_watch = less_sources,
|
||||||
|
ts_config = './tsconfig.json',
|
||||||
|
ts_sources = './src/typescript/**/*.ts',
|
||||||
|
ts_watch = ts_sources;
|
||||||
|
|
||||||
|
// default task
|
||||||
|
gulp.task('default', ['less', 'typescript']);
|
||||||
|
|
||||||
|
// watcher
|
||||||
|
gulp.task('watch', function () {
|
||||||
|
gulp.watch(less_watch, ['less']);
|
||||||
|
gulp.watch(ts_watch, ['typescript']);
|
||||||
|
});
|
||||||
|
|
||||||
|
// less
|
||||||
|
gulp.task('less', function () {
|
||||||
|
return gulp.src(less_sources)
|
||||||
|
.pipe(less({
|
||||||
|
paths: [
|
||||||
|
path.join(__dirname, 'less', 'includes')
|
||||||
|
],
|
||||||
|
compress: true
|
||||||
|
}))
|
||||||
|
.pipe(concat('app.css'))
|
||||||
|
.pipe(gulp.dest(destination));
|
||||||
|
});
|
||||||
|
|
||||||
|
// typescript
|
||||||
|
gulp.task('typescript', function () {
|
||||||
|
var tsProject = ts.createProject(ts_config);
|
||||||
|
|
||||||
|
return gulp.src(ts_sources)
|
||||||
|
.pipe(ts(tsProject))
|
||||||
|
.pipe(jsmin({
|
||||||
|
ext: {
|
||||||
|
src: '-',
|
||||||
|
min: '.js'
|
||||||
|
},
|
||||||
|
noSource: true
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest(destination));
|
||||||
|
});
|
10
package.json
Normal file
10
package.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"gulp": "^3.9.1",
|
||||||
|
"gulp-concat": "^2.6.0",
|
||||||
|
"gulp-less": "^3.0.5",
|
||||||
|
"gulp-minify": "0.0.11",
|
||||||
|
"gulp-typescript": "^2.13.0"
|
||||||
|
}
|
||||||
|
}
|
31
public/get.php
Normal file
31
public/get.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
use LastFmApi\Api\AuthApi;
|
||||||
|
use LastFmApi\Api\UserApi;
|
||||||
|
|
||||||
|
function view($object)
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
return json_encode($object, JSON_NUMERIC_CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists('../vendor/autoload.php')) {
|
||||||
|
die(view(['error' => 'Please run "composer install" in the main directory first!']));
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once '../vendor/autoload.php';
|
||||||
|
|
||||||
|
if (!file_exists('../config.ini')) {
|
||||||
|
die(view(['error' => 'Configuration missing! Make a copy of config.example.ini named config.ini and set your API key.']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = parse_ini_file('../config.ini');
|
||||||
|
|
||||||
|
$auth = new AuthApi('setsession', ['apiKey' => $config['api_key']]);
|
||||||
|
$user = new UserApi($auth);
|
||||||
|
$now = $user->getRecentTracks(['user' => (isset($_GET['u']) ? $_GET['u'] : ''), 'limit' => '1']);
|
||||||
|
|
||||||
|
if ($now === false) {
|
||||||
|
$now = ['error' => 'User not found.'];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo view($now);
|
45
public/index.html
Normal file
45
public/index.html
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
|
<title>Now Listening</title>
|
||||||
|
<meta name="format-detection" content="telephone=no">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Exo+2:400,400italic,200,200italic" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="/app.css" type="text/css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="background" id="background"></div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="container__index hidden" id="index">
|
||||||
|
<div class="index__title">Now Listening</div>
|
||||||
|
<div class="index__description">
|
||||||
|
Enter your Last.FM username in the box below!
|
||||||
|
</div>
|
||||||
|
<div class="index__form">
|
||||||
|
<input class="index__username" type="text" id="username" placeholder="flashwave_">
|
||||||
|
<button class="index__submit fa fa-forward" id="submit"></button>
|
||||||
|
</div>
|
||||||
|
<div class="index__dev">
|
||||||
|
<a class="fa fa-code" href="https://github.com/flashwave/now-listening" title="Written"></a> by <a class="fa fa-flash" href="https://flash.moe" title="flash"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container__user hidden" id="user">
|
||||||
|
<div class="cover">
|
||||||
|
<div class="cover__image" id="np_cover"></div>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<div class="info__title" id="np_title"></div>
|
||||||
|
<div class="info__artist" id="np_artist"></div>
|
||||||
|
<div class="info__flags" id="np_flags"></div>
|
||||||
|
<div class="info__user">
|
||||||
|
<div class="info__user-back" id="np_back">Back</div>
|
||||||
|
<div class="info__user-name" id="np_user"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/app.js" type="text/javascript"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
public/resources/grid.png
Normal file
BIN
public/resources/grid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 945 B |
BIN
public/resources/no-cover.png
Normal file
BIN
public/resources/no-cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
174
src/less/app.less
Normal file
174
src/less/app.less
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background: url('resources/grid.png') transparent;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
color: #fff;
|
||||||
|
font: 12px/20px "Exo 2", sans-serif;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
&__index,
|
||||||
|
&__user {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__index {
|
||||||
|
text-align: center;
|
||||||
|
background: fade(#111, 80%);
|
||||||
|
box-shadow: 0 1px 4px #111;
|
||||||
|
padding: 6px 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__user {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
box-shadow: 0 1px 4px #111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
background: none no-repeat center / cover #000;
|
||||||
|
z-index: -1;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
transition: background-color 2.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index {
|
||||||
|
&__title {
|
||||||
|
font-weight: 200;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 3em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__form {
|
||||||
|
margin: 5px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 1px 4px #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__username,
|
||||||
|
&__submit {
|
||||||
|
border: 1px solid #111;
|
||||||
|
color: #fff;
|
||||||
|
padding: 2px;
|
||||||
|
font-size: 1.5em;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__username {
|
||||||
|
background: fade(#111, 50%);
|
||||||
|
border-right: 0;
|
||||||
|
font-family: "Exo 2", sans-serif;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__submit {
|
||||||
|
border-left: 0;
|
||||||
|
background: #111;
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__dev {
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
background: none no-repeat center / cover fade(#111, 80%);
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
background: fade(#111, 80%);
|
||||||
|
padding: 6px 8px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
|
||||||
|
@media (min-width: 601px) {
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 3em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__artist {
|
||||||
|
font-size: 1.5em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__flags {
|
||||||
|
line-height: 1.5em;
|
||||||
|
font-size: 2em;
|
||||||
|
flex-grow: 1; // fill space
|
||||||
|
}
|
||||||
|
|
||||||
|
&__user {
|
||||||
|
align-self: end;
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&-back {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
137
src/typescript/AJAX.ts
Normal file
137
src/typescript/AJAX.ts
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
namespace NP
|
||||||
|
{
|
||||||
|
export class AJAX
|
||||||
|
{
|
||||||
|
// XMLHTTPRequest container
|
||||||
|
private Request: XMLHttpRequest;
|
||||||
|
private Callbacks: any;
|
||||||
|
private Headers: any;
|
||||||
|
private URL: string;
|
||||||
|
private Send: string = null;
|
||||||
|
private Asynchronous: boolean = true;
|
||||||
|
|
||||||
|
// Prepares the XMLHttpRequest and stuff
|
||||||
|
constructor(async: boolean = true) {
|
||||||
|
this.Request = new XMLHttpRequest();
|
||||||
|
this.Callbacks = new Object();
|
||||||
|
this.Headers = new Object();
|
||||||
|
this.Asynchronous = async;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start
|
||||||
|
public Start(method: HTTPMethod, avoidCache: boolean = false): void {
|
||||||
|
// Open the connection
|
||||||
|
this.Request.open(HTTPMethod[method], this.URL + (avoidCache ? "?no-cache=" + Date.now() : ""), this.Asynchronous);
|
||||||
|
|
||||||
|
// Set headers
|
||||||
|
this.PrepareHeaders();
|
||||||
|
|
||||||
|
// Watch the ready state
|
||||||
|
this.Request.onreadystatechange = () => {
|
||||||
|
// Only invoke when complete
|
||||||
|
if (this.Request.readyState === 4) {
|
||||||
|
// Check if a callback if present
|
||||||
|
if ((typeof this.Callbacks[this.Request.status]).toLowerCase() === 'function') {
|
||||||
|
this.Callbacks[this.Request.status]();
|
||||||
|
} else { // Else check if there's a generic fallback present
|
||||||
|
if ((typeof this.Callbacks['0']).toLowerCase() === 'function') {
|
||||||
|
// Call that
|
||||||
|
this.Callbacks['0']();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Request.send(this.Send);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop
|
||||||
|
public Stop(): void {
|
||||||
|
this.Request = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add post data
|
||||||
|
public SetSend(data: any): void {
|
||||||
|
// Storage array
|
||||||
|
var store: Array<string> = new Array<string>();
|
||||||
|
|
||||||
|
// Iterate over the object and them in the array with an equals sign inbetween
|
||||||
|
for (var item in data) {
|
||||||
|
store.push(encodeURIComponent(item) + "=" + encodeURIComponent(data[item]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to send
|
||||||
|
this.Send = store.join('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set raw post
|
||||||
|
public SetRawSend(data: string): void {
|
||||||
|
this.Send = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get response
|
||||||
|
public Response(): string {
|
||||||
|
return this.Request.responseText;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all headers
|
||||||
|
public ResponseHeaders(): string {
|
||||||
|
return this.Request.getAllResponseHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a header
|
||||||
|
public ResponseHeader(name: string): string {
|
||||||
|
return this.Request.getResponseHeader(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set charset
|
||||||
|
public ContentType(type: string, charset: string = null): void {
|
||||||
|
this.AddHeader('Content-Type', type + ';charset=' + (charset ? charset : 'utf-8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a header
|
||||||
|
public AddHeader(name: string, value: string): void {
|
||||||
|
// Attempt to remove a previous instance
|
||||||
|
this.RemoveHeader(name);
|
||||||
|
|
||||||
|
// Add the new header
|
||||||
|
this.Headers[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a header
|
||||||
|
public RemoveHeader(name: string): void {
|
||||||
|
if ((typeof this.Headers[name]).toLowerCase() !== 'undefined') {
|
||||||
|
delete this.Headers[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare Request headers
|
||||||
|
public PrepareHeaders(): void {
|
||||||
|
for (var header in this.Headers) {
|
||||||
|
this.Request.setRequestHeader(header, this.Headers[header]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a callback
|
||||||
|
public AddCallback(status: number, callback: Function): void {
|
||||||
|
// Attempt to remove previous instances
|
||||||
|
this.RemoveCallback(status);
|
||||||
|
|
||||||
|
// Add the new callback
|
||||||
|
this.Callbacks[status] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a callback
|
||||||
|
public RemoveCallback(status: number): void {
|
||||||
|
// Delete the callback if present
|
||||||
|
if ((typeof this.Callbacks[status]).toLowerCase() === 'function') {
|
||||||
|
delete this.Callbacks[status];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the URL
|
||||||
|
public SetUrl(url: string): void {
|
||||||
|
this.URL = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
src/typescript/Background.ts
Normal file
8
src/typescript/Background.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace NP
|
||||||
|
{
|
||||||
|
export enum Background
|
||||||
|
{
|
||||||
|
IMAGE,
|
||||||
|
COLOURFADE
|
||||||
|
}
|
||||||
|
}
|
134
src/typescript/DOM.ts
Normal file
134
src/typescript/DOM.ts
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
namespace NP
|
||||||
|
{
|
||||||
|
export class DOM
|
||||||
|
{
|
||||||
|
// Block Element Modifier class name generator
|
||||||
|
public static BEM(block: string, element: string = null, modifiers: string[] = [], firstModifierOnly: boolean = false): string
|
||||||
|
{
|
||||||
|
var className: string = "";
|
||||||
|
|
||||||
|
if (firstModifierOnly && modifiers.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
className += block;
|
||||||
|
|
||||||
|
if (element !== null) {
|
||||||
|
className += "__" + element;
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseName: string = className;
|
||||||
|
|
||||||
|
for (var _i in modifiers) {
|
||||||
|
if (firstModifierOnly) {
|
||||||
|
return baseName + "--" + modifiers[_i];
|
||||||
|
}
|
||||||
|
|
||||||
|
className += " " + baseName + "--" + modifiers[_i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand for creating an element with a class and string
|
||||||
|
public static Element(name: string, className: string = null, id: string = null): HTMLElement {
|
||||||
|
var element = document.createElement(name);
|
||||||
|
|
||||||
|
if (className !== null) {
|
||||||
|
element.className = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id !== null) {
|
||||||
|
element.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand for textnode
|
||||||
|
public static Text(text: string): Text {
|
||||||
|
return document.createTextNode(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand for getElementById (i'm lazy)
|
||||||
|
public static ID(id: string): HTMLElement {
|
||||||
|
return document.getElementById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand for removing an element
|
||||||
|
public static Remove(element: HTMLElement): void {
|
||||||
|
element.parentNode.removeChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand for the first element of getElementsByClassName
|
||||||
|
public static Class(className: string): NodeListOf<HTMLElement> {
|
||||||
|
return <NodeListOf<HTMLElement>>document.getElementsByClassName(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand for prepending
|
||||||
|
public static Prepend(target: HTMLElement, element: HTMLElement | Text): void {
|
||||||
|
if (target.children.length) {
|
||||||
|
target.insertBefore(element, target.firstChild);
|
||||||
|
} else {
|
||||||
|
this.Append(target, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand for appending
|
||||||
|
public static Append(target: HTMLElement, element: HTMLElement | Text): void {
|
||||||
|
target.appendChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting all classes of an element
|
||||||
|
public static ClassNames(target: HTMLElement): string[] {
|
||||||
|
var className: string = target.className,
|
||||||
|
classes: string[] = [];
|
||||||
|
|
||||||
|
if (className.length > 1) {
|
||||||
|
classes = className.split(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding classes to an element
|
||||||
|
public static AddClass(target: HTMLElement, classes: string[]): void {
|
||||||
|
for (var _i in classes) {
|
||||||
|
var current: string[] = this.ClassNames(target),
|
||||||
|
index: number = current.indexOf(classes[_i]);
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.push(classes[_i]);
|
||||||
|
|
||||||
|
target.className = current.join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removing classes
|
||||||
|
public static RemoveClass(target: HTMLElement, classes: string[]): void {
|
||||||
|
for (var _i in classes) {
|
||||||
|
var current: string[] = this.ClassNames(target),
|
||||||
|
index: number = current.indexOf(classes[_i]);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.splice(index, 1);
|
||||||
|
|
||||||
|
target.className = current.join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Clone(subject: HTMLElement): HTMLElement {
|
||||||
|
return (<HTMLElement>subject.cloneNode(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Query(query: string): NodeListOf<Element> {
|
||||||
|
return document.querySelectorAll(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/typescript/HTTPMethod.ts
Normal file
11
src/typescript/HTTPMethod.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace NP
|
||||||
|
{
|
||||||
|
export enum HTTPMethod
|
||||||
|
{
|
||||||
|
GET,
|
||||||
|
HEAD,
|
||||||
|
POST,
|
||||||
|
PUT,
|
||||||
|
DELETE
|
||||||
|
}
|
||||||
|
}
|
13
src/typescript/Initialisation.ts
Normal file
13
src/typescript/Initialisation.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
window.addEventListener("load", () => {
|
||||||
|
var user: string = location.hash.substring(2);
|
||||||
|
|
||||||
|
NP.UI.RegisterHooks();
|
||||||
|
|
||||||
|
if (user.length < 1) {
|
||||||
|
NP.UI.Mode(NP.Mode.INDEX);
|
||||||
|
NP.UI.Background(NP.Background.COLOURFADE);
|
||||||
|
} else {
|
||||||
|
NP.UI.Mode(NP.Mode.USER);
|
||||||
|
NP.Watcher.Start(user);
|
||||||
|
}
|
||||||
|
});
|
8
src/typescript/Mode.ts
Normal file
8
src/typescript/Mode.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace NP
|
||||||
|
{
|
||||||
|
export enum Mode
|
||||||
|
{
|
||||||
|
INDEX,
|
||||||
|
USER
|
||||||
|
}
|
||||||
|
}
|
114
src/typescript/UI.ts
Normal file
114
src/typescript/UI.ts
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
namespace NP
|
||||||
|
{
|
||||||
|
export class UI
|
||||||
|
{
|
||||||
|
private static IndexElem: HTMLDivElement = <HTMLDivElement>DOM.ID('index');
|
||||||
|
private static UserElem: HTMLDivElement = <HTMLDivElement>DOM.ID('user');
|
||||||
|
|
||||||
|
private static BGElem: HTMLDivElement = <HTMLDivElement>DOM.ID('background');
|
||||||
|
|
||||||
|
private static FormUsername: HTMLInputElement = <HTMLInputElement>DOM.ID('username');
|
||||||
|
private static FormSubmit: HTMLButtonElement = <HTMLButtonElement>DOM.ID('submit');
|
||||||
|
|
||||||
|
private static InfoCover: HTMLDivElement = <HTMLDivElement>DOM.ID('np_cover');
|
||||||
|
private static InfoTitle: HTMLDivElement = <HTMLDivElement>DOM.ID('np_title');
|
||||||
|
private static InfoArtist: HTMLDivElement = <HTMLDivElement>DOM.ID('np_artist');
|
||||||
|
private static InfoUser: HTMLDivElement = <HTMLDivElement>DOM.ID('np_user');
|
||||||
|
private static InfoBack: HTMLDivElement = <HTMLDivElement>DOM.ID('np_back');
|
||||||
|
private static InfoFlags: HTMLDivElement = <HTMLDivElement>DOM.ID('np_flags');
|
||||||
|
|
||||||
|
private static ColourFadeInterval: number = null;
|
||||||
|
|
||||||
|
public static Mode(mode: Mode): void {
|
||||||
|
switch (mode) {
|
||||||
|
case Mode.INDEX:
|
||||||
|
DOM.AddClass(this.UserElem, ['hidden']);
|
||||||
|
DOM.RemoveClass(this.IndexElem, ['hidden']);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Mode.USER:
|
||||||
|
DOM.AddClass(this.IndexElem, ['hidden']);
|
||||||
|
DOM.RemoveClass(this.UserElem, ['hidden']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Background(mode: Background, property: string = null): void {
|
||||||
|
switch (mode) {
|
||||||
|
case Background.COLOURFADE:
|
||||||
|
if (this.ColourFadeInterval !== null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.BGElem.style.backgroundImage = null;
|
||||||
|
|
||||||
|
var fader: Function = () => {
|
||||||
|
var colour: string = Math.floor(Math.random() * 16777215).toString(16);
|
||||||
|
|
||||||
|
if (colour.length !== 6 && colour.length !== 3) {
|
||||||
|
colour = "000000".substring(colour.length) + colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
UI.BGElem.style.backgroundColor = "#" + colour;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ColourFadeInterval = setInterval(fader, 2000);
|
||||||
|
fader.call(this);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Background.IMAGE:
|
||||||
|
if (property === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.ColourFadeInterval !== null) {
|
||||||
|
clearInterval(this.ColourFadeInterval);
|
||||||
|
this.ColourFadeInterval = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.BGElem.style.backgroundColor = null;
|
||||||
|
this.BGElem.style.backgroundImage = "url('{0}')".replace('{0}', property);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SetInfo(cover: string, title: string, artist: string, user: string, now: boolean = false): void {
|
||||||
|
this.Background(Background.IMAGE, cover);
|
||||||
|
this.InfoCover.style.backgroundImage = "url('{0}')".replace('{0}', cover);
|
||||||
|
this.InfoTitle.innerText = title;
|
||||||
|
this.InfoArtist.innerText = artist;
|
||||||
|
this.InfoUser.innerText = user;
|
||||||
|
this.InfoFlags.innerHTML = '';
|
||||||
|
|
||||||
|
if (now) {
|
||||||
|
var nowIcon: HTMLSpanElement = DOM.Element('span', 'fa fa-music');
|
||||||
|
nowIcon.title = 'Now playing';
|
||||||
|
this.InfoFlags.appendChild(nowIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RegisterHooks(): void {
|
||||||
|
UI.InfoBack.addEventListener('click', () => {
|
||||||
|
Watcher.Stop();
|
||||||
|
UI.Mode(Mode.INDEX);
|
||||||
|
NP.UI.Background(NP.Background.COLOURFADE);
|
||||||
|
location.hash = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
var enter: Function = () => {
|
||||||
|
location.hash = '#/' + UI.FormUsername.value;
|
||||||
|
Watcher.Start(UI.FormUsername.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
UI.FormSubmit.addEventListener('click', () => {
|
||||||
|
enter.call(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
UI.FormUsername.addEventListener('keydown', (ev) => {
|
||||||
|
if (ev.keyCode === 13) {
|
||||||
|
enter.call(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
src/typescript/Watcher.ts
Normal file
53
src/typescript/Watcher.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
namespace NP
|
||||||
|
{
|
||||||
|
export class Watcher
|
||||||
|
{
|
||||||
|
private static Fetcher: AJAX = null;
|
||||||
|
private static CheckIntervalId: number = null;
|
||||||
|
private static CheckTimeout: number = 60 * 1000;
|
||||||
|
private static GetPath: string = "/get.php";
|
||||||
|
private static User: string = null;
|
||||||
|
|
||||||
|
public static Start(user: string): void {
|
||||||
|
this.User = user;
|
||||||
|
|
||||||
|
this.Fetcher = new AJAX;
|
||||||
|
|
||||||
|
this.Fetcher.AddCallback(200, () => {
|
||||||
|
var data: any = JSON.parse(Watcher.Fetcher.Response());
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
if ((typeof data.error).toLowerCase() !== 'undefined') {
|
||||||
|
Watcher.Stop();
|
||||||
|
UI.Mode(Mode.INDEX);
|
||||||
|
NP.UI.Background(NP.Background.COLOURFADE);
|
||||||
|
alert(data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UI.Mode(Mode.USER);
|
||||||
|
|
||||||
|
var image: string = data[0].images.large.length < 1 ? '/resources/no-cover.png' : data[0].images.large.replace('/174s/', '/300s/'),
|
||||||
|
now: boolean = (typeof data[0].nowplaying).toLowerCase() === 'undefined' ? false : data[0].nowplaying;
|
||||||
|
|
||||||
|
UI.SetInfo(image, data[0].name, data[0].artist.name, this.User, now);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.Check();
|
||||||
|
|
||||||
|
this.CheckIntervalId = setInterval(this.Check, this.CheckTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stop(): void {
|
||||||
|
clearInterval(this.CheckIntervalId);
|
||||||
|
this.CheckIntervalId = null;
|
||||||
|
this.Fetcher = null;
|
||||||
|
this.User = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Check(): void {
|
||||||
|
Watcher.Fetcher.SetUrl(Watcher.GetPath + "?u=" + Watcher.User);
|
||||||
|
Watcher.Fetcher.Start(HTTPMethod.GET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"removeComments": true,
|
||||||
|
"preserveConstEnums": true,
|
||||||
|
"outFile": "app.js",
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"public"
|
||||||
|
],
|
||||||
|
"compileOnSave": true
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue