Browse Source

before:auth

windhamdavid 4 years ago
parent
commit
875ae44c36
10 changed files with 518 additions and 3330 deletions
  1. 12 0
      .jshintrc
  2. 20 5
      gulpfile.js
  3. 2 1
      package.json
  4. 23 35
      src/css/main.css
  5. BIN
      src/img/1.png
  6. 93 83
      src/index.html
  7. 5 3141
      src/js/amplitude-v2.2.0.js
  8. 325 0
      src/js/bootstrap-validator-v0.9.0.js
  9. 0 3
      src/js/chat.js
  10. 38 62
      src/js/radio.js

+ 12 - 0
.jshintrc

@@ -0,0 +1,12 @@
+{
+	"node": true,
+	"esnext": true,
+	"bitwise": true,
+	"curly": true,
+	"immed": true,
+	"newcap": true,
+	"noarg": true,
+	"undef": true,
+	"unused": "vars",
+	"strict": true
+}

+ 20 - 5
gulpfile.js

@@ -2,6 +2,7 @@
 
 var gulp = require('gulp');
 var nodemon = require('gulp-nodemon');
+var jshint = require('gulp-jshint');
 var uglify = require('gulp-uglify');
 var minifyHTML = require('gulp-minify-html');
 var minifycss = require('gulp-minify-css');
@@ -9,12 +10,20 @@ var concat = require('gulp-concat');
 var del = require('del');
 
 
-gulp.task('test', function(){
+var hintTask = function () {
+  gulp.src(['*.js', './src/js/**/*.js'])
+    .pipe(jshint('.jshintrc'))
+    .pipe(jshint.reporter('jshint-stylish'));
+  console.log('HINT: Hint Complete');
+};
 
+gulp.task('hint', function() {
+  hintTask();
 });
 
 
 var cleanTask = function () {
+  console.log('CLEAN: Clean Complete');
   return del([
     'app/**/*',
     '!app/img/',
@@ -22,7 +31,6 @@ var cleanTask = function () {
     '!app/fonts/',
     '!app/js/',
   ]);
-  console.log('CLEAN: Clean Complete');
 };
 
 gulp.task('clean', function() {
@@ -44,7 +52,14 @@ var buildTask = function() {
     .pipe(concat('style.min.css'))
     .pipe(minifycss())
     .pipe(gulp.dest('./app/css'));
-  gulp.src(['./src/js/jquery-v2.1.4.js', './src/js/bootstrap-v3.3.6.js', './src/js/bootstrap-progress-v0.9.0.js', './src/js/underscore-v1.8.3.js', './src/js/handlebars-v4.0.5.js'])
+  gulp.src([
+      './src/js/jquery-v2.1.4.js',
+      './src/js/bootstrap-v3.3.6.js',
+      './src/js/bootstrap-validator-v0.9.0.js',
+      './src/js/bootstrap-progress-v0.9.0.js',
+      './src/js/underscore-v1.8.3.js',
+      './src/js/handlebars-v4.0.5.js'
+  ])
     .pipe(concat('vendor.min.js'))
     .pipe(uglify())
     .pipe(gulp.dest('./app/js'));
@@ -54,9 +69,9 @@ var buildTask = function() {
     .pipe(gulp.dest('./app/js'));
   gulp.src('./src/js/templates/*')
     .pipe(gulp.dest('./app/js/templates'));
-  gulp.src(['.src/js/amplitude-v2.2.0.js', './src/js/chat.js', './src/js/radio.js'])
+  gulp.src(['./src/js/chat.js', './src/js/amplitude-v2.2.0.js', './src/js/radio.js'])
     .pipe(concat('radio.min.js'))
-    //.pipe(uglify())
+    .pipe(uglify())
     .pipe(gulp.dest('./app/js'));
   console.log('BUILD: Build Complete');
 };

+ 2 - 1
package.json

@@ -18,9 +18,10 @@
     "gulp": "^3.9.0",
     "gulp-concat": "^2.6.0",
     "gulp-jshint": "^2.0.0",
+    "gulp-uglify": "^1.5.1",
+    "jshint-stylish": "^2.1.0",
     "gulp-minify-css": "^1.2.1",
     "gulp-minify-html": "^1.0.4",
-    "gulp-uglify": "^1.5.1",
     "gulp-nodemon": "^2.0.4",
     "nodemon": "^1.8.1"
   },

+ 23 - 35
src/css/main.css

@@ -38,7 +38,17 @@ header {
     background: rgba (145,145,145,.5);
 }
 
-
+#auth-modal {
+  background: #000;
+  color: #fff;
+}
+#auth-modal .modal-content{
+  background: #333;
+  color: #fff;
+}
+#modal_setnick {
+  background: rgba(0,0,0,.8);
+}
 
 /***********************************
 ========== Audio Player ============ 
@@ -377,14 +387,7 @@ meter {
   background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
   background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
 }
-.progress-bar-warning {
-  background-color: #f0ad4e;
-}
-.progress-striped .progress-bar-warning {
-  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
-}
+
 .progress-bar-danger {
   background-color: #d9534f;
 }
@@ -417,18 +420,23 @@ li#Lobby_tab {
   display:none;
 }
 
+.social-comments {
+  min-height:355px;
+}
 #fb_comments { 
-  height:380px; 
   padding-bottom:10px; 
   overflow: scroll; 
 }
 .fb-comments, .fb-comments iframe[style], .fb-comments span {
-   width: 100% !important;
+  width: 100% !important;
+}
+.audio {
+  margin: 0 0 30px;
+  
 }
-
 video {
   background:#000;
-  height: 260px;
+  height: 256px;
   margin: 0 5px 5px 0;
   vertical-align: top;
   width: calc(50% - 12px);
@@ -439,6 +447,7 @@ video {
 
 
 
+
 /***********************************
 ======= Playlist/Calendar ========== 
 ***********************************/
@@ -498,31 +507,10 @@ ul .badge {
 
 
 /***********************************
-=========== Chat Rooms ============= 
+============ Footer =============== 
 ***********************************/
 
-/* =========  Vertical Align Modals ============ */
 
-.vertical-alignment-helper {
-    display:table;
-    height: 100%;
-    width: 100%;
-    pointer-events:none; /* This makes sure that we can still click outside of the modal to close it */
-}
-.vertical-align-center {
-    /* To center vertically */
-    display: table-cell;
-    vertical-align: middle;
-    pointer-events:none;
-}
-.modal-content {
-    /* Bootstrap sets the size of the modal in the modal-dialog class, we need to inherit it */
-    width:inherit;
-    height:inherit;
-    /* To center horizontally */
-    margin: 0 auto;
-    pointer-events: all;
-}
 
 
 /* =========  Footer/Bottom Nav ============ */

BIN
src/img/1.png


+ 93 - 83
src/index.html

@@ -112,6 +112,34 @@
                   </div>          
                </div>
                <!-- END Playlist -->
+               
+               <!-- START Connection Error Modal -->
+               <div class="modal fade" id="connection-error" tabindex="-1" role="dialog" aria-labelledby="support" aria-hidden="true">
+                 <div class="modal-dialog" role="document">
+                   <div class="modal-content">
+                     <div class="modal-header">
+                       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                       <h4 class="modal-title">Off the Air</h4>
+                     </div>
+                     <div class="modal-body">
+                        <div class="alert alert-danger" role="alert">
+                           <p><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <b>Connection Error</b>:</p> 
+                           <div class="progress" id="error-reconnecting">
+                             <div class="progress-bar progress-bar-danger progress-bar-striped" id="connection-error-reconnecting" role="progressbar" aria-valuemin="0" aria-valuemax="100" data-transitiongoal="100">
+                           </div> 
+                           
+                        </div>
+                        <p><span id="error-reconnecting-again"class="label label-danger">ERROR</span></p>
+                        <p>Unable to connect with <a href="http://stream.davidawindham.com/"><i>stream.davidawindham.com</i></a>. I only play music when I'm listening to it live and the rest of the time the stream is offline. There is no reason to be here unless I am. Try again some other time or <a href="https://davidawindham.com/contact">contact me</a>.</p>
+                        </div>
+                     </div>
+                     <div class="modal-footer">
+                        <button type="button" id="connection-error-retry" class="btn btn-danger">Retry</button>
+                     </div>
+                   </div>
+                 </div>
+               </div>
+               <!-- END Connection Error Modal -->
          
          </section>
    	</div>
@@ -131,7 +159,7 @@
                                 <li><a href="#modal_joinroom" data-toggle="modal"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>&nbsp;Join room</a></li>
                                 <li><a id="b_leave_room" href="#"><span class="glyphicon glyphicon-minus-sign" aria-hidden="true"></span>&nbsp;Leave room</a></li>
                                 <li class="divider"></li>
-                                <li><a href="#modal_setnick" data-toggle="modal"><span class="glyphicon glyphicon-user" aria-hidden="true"></span>&nbsp;Set nickname</a></li>
+
                             </ul>
                         </li>
                         <li id="Lobby_tab" class="active"><a href="#Lobby" data-toggle="tab" data-original-title="Chat" data-placement="bottom"><span class="glyphicon glyphicon-comment" aria-hidden="true"></span><span class="hidden-tab">Lobby</span></a></li>
@@ -180,7 +208,7 @@
                    <div class="tab-pane" id="social">
                       <div id="fb-root"></div>
                       <script>(function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) return;js = d.createElement(s); js.id = id;js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.5";fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk'));</script>
-                      <div class="well">           
+                      <div class="well social-comments">           
                          <div class="fb-comments" data-href="https://www.facebook.com/davidawindham" data-numposts="5" data-colorscheme="dark"></div>
                       </div>
                    </div>
@@ -190,7 +218,7 @@
                    <div class="tab-pane" id="call">
                       <div id="audio">
                          <div class="well">
-                            <div class="row-fluid">
+                            <div class="row-fluid audio">
                               <p><span class="label label-primary">Local audio:</span></p>
                               <audio id="audio1" autoplay controls muted></audio>
                               <p><span class="label label-primary">Remote audio:</span></p>
@@ -198,7 +226,7 @@
                            </div>
                            <div class="row-fluid">
                                  <p><b>Help</b>: This audio/video call features are only supported by the most recent versions of Mozilla Firefox and Google Chrome</p>
-                                 <p><b>803-712-3283(DAVE)</b>: You can also call this number to reach me.</p>
+                                 <p>You can also call <b>803-712-3283(DAVE)</b> to reach me</p>
                            </div>
                               
                           </div>
@@ -261,26 +289,58 @@
                </div>
             </div>
             
-            <div id="modal_setnick" class="modal fade">
+            <div id="modal_setnick" class="modal fade" data-keyboard="false" data-backdrop="static" >
                <div class="vertical-alignment-helper">
                   <div class="modal-dialog modal-sm vertical-align-center">
                       <div class="modal-content">
-                         <div class="modal-header">
-                             <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-                             <h3>Set a Nickname</h3>
-                         </div>
-                         <div class="modal-body">
-                             <input id="nickname" type="text" class="form-control" placeholder="Type your nickname here">
-                         </div>
-                         <div class="modal-footer">
-                             <a id="b_set_nickname" href="#" class="btn btn-success">Set a Nickname</a>
-                         </div>
+                         <form data-toggle="validator" role="form">
+                            <div class="modal-body">
+                               <div class="form-group has-feedback">
+                                  <label class="control-label" for="inputSuccess2">Set a Nickname:</label>
+                                  <div class="input-group">
+                                     <span class="input-group-addon">@</span>
+                                     <input id="nickname" type="text" class="form-control" pattern="^[a-zA-Z]+$" minlength="3" maxlength="25" placeholder="letters only (min 3)" required>
+                                  </div>
+                                  <span class="glyphicon glyphicon-ok form-control-feedback" aria-hidden="true"></span>
+                               </div>
+                            </div>
+                            <div class="modal-footer">
+                              <div class="form-group">
+                                <button id="b_set_nickname" type="submit" class="btn btn-success">OK</button>
+                              </div>
+                            </div>
+                         </form>
                      </div>
                   </div>
                </div>
             </div>
             <!-- End Modal -->
             
+            <!-- START Auth Modal -->
+            <div class="modal fade" id="auth-modal" data-keyboard="false" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="support" aria-hidden="true">
+              <div class="modal-dialog" role="document">
+                <div class="modal-content">
+                  <div class="modal-header">
+                    <h4 class="modal-title"><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> On Air</h4>
+                  </div>
+                  <div class="modal-body">
+                     <h5>Get Off My Lawn!</h5>
+                     <ol>
+                        <li>This site is for demonstration purposes.</li>
+                        <li>There is no reason to be here unless I am.</li>
+                        <li>If you don't know me, you shouldn't be here.</li>
+                        <li>Do Not Share this URL with Anyone.</li>
+                        <li>Do Not Post this URL Anywhere (that means you Dad... no Facebook!)</li>
+                     </ol>                     
+                  </div>
+                  <div class="modal-footer">
+                     <button type="submit" class="btn btn-success" data-dismiss="modal" data-toggle="modal" data-target="#modal_setnick">Understood</button>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <!-- END Auth Modal -->
+            
          </section>
       </div>
       <!-- End Connect -->
@@ -309,32 +369,7 @@
 </div>
 <!-- END Footer NAV -->
 
-<!-- START Auth Modal -->
-<div class="modal fade" id="auth-modal" tabindex="-1" role="dialog" aria-labelledby="support" aria-hidden="true">
-  <div class="modal-dialog" role="document">
-    <div class="modal-content">
-      <div class="modal-header">
-        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-        <h4 class="modal-title" id="about"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> Off the Air</h4>
-      </div>
-      <div class="modal-body">
-         <div class="alert alert-danger" role="alert">
-            <b>Connection Error</b>: Unable to connect with <a href="http://stream.davidawindham.com/"><i>stream.davidawindham.com</i></a>. 
-            <div class="progress" id="error-reconnecting">
-              <div class="progress-bar progress-bar-danger progress-bar-striped" id="connection-error-reconnecting" role="progressbar" aria-valuemin="0" aria-valuemax="100" data-transitiongoal="100">
-            </div>  
-         </div>
-         <p>I only play music when I'm listening to it live and the rest of the time the stream is offline. There is no reason to be here unless I am. Try again some other time, check the schedule, or <a href="https://davidawindham.com/contact">contact me</a>.</p>
-         </div>
-      </div>
-      <div class="modal-footer">
-         <button type="button" id="connection-error-retry" class="btn btn-danger">Retry</button>
-         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-      </div>
-    </div>
-  </div>
-</div>
-<!-- END Auth Modal -->
+
 
 <!-- START Listeners Modal -->
 <div class="modal fade" id="connected" tabindex="-1" role="dialog" aria-labelledby="connected" aria-hidden="true">
@@ -368,21 +403,21 @@
 
 <!-- START Info Modal -->
 <div class="modal fade" id="about" tabindex="-1" role="dialog" aria-labelledby="about" aria-hidden="true">
-  <div class="modal-dialog" role="document">
-    <div class="modal-content">
-      <div class="modal-header">
-        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-        <h4 class="modal-title" id="about">About</h4>
-      </div>
-      <div class="modal-body">
-        <p>This is just a little place I can share some music and talk with friends. I enjoy listening to music and I spend quite a bit of time doing so. I like to tune into terrestrial stations that stream to help find new music, but I’ve found almost all of the robot mixes from the big streaming providers unsatisfying.</p>
-        <p><b>Kudos</b>: This page is powered by <a href="">Linode</a>, <a href="">Node.js</a>, <a href="">Express</a>, <a href="">Socket.io</a>, and <a href="">Icecast-kh</a>.</p>
-      </div>
-      <div class="modal-footer">
-        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+   <div class="modal-dialog" role="document">
+      <div class="modal-content">
+         <div class="modal-header">
+            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+            <h4 class="modal-title">About</h4>
+         </div>
+         <div class="modal-body">
+            <p>This is just a little place I can share some music and talk with friends. I enjoy listening to music and I spend quite a bit of time doing so. I like to tune into terrestrial stations that stream to help find new music, but I’ve found almost all of the robot mixes from the big streaming providers unsatisfying.</p>
+            <p><b>Kudos</b>: This page is powered by <a href="">Linode</a>, <a href="">Node.js</a>, <a href="">Express</a>, <a href="">Socket.io</a>, and <a href="">Icecast-kh</a>.</p>
+         </div>
+         <div class="modal-footer">
+            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+         </div>
       </div>
-    </div>
-  </div>
+   </div>
 </div>
 <!-- END Info Modal -->
 
@@ -392,7 +427,7 @@
     <div class="modal-content">
       <div class="modal-header">
         <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-        <h4 class="modal-title" id="about">Help</h4>
+        <h4 class="modal-title">Help</h4>
       </div>
       <div class="modal-body">
         <p><b>Audio Streaming</b>: There are a number of ways you can listen to the audio stream. This page uses the HTML5 audio element to stream it to your computer or phone. You can also point your favorite media player to the stream at <a href="http://stream.davidawindham.com/stream">http://stream.davidawindham.com/</a>.</p>
@@ -412,7 +447,7 @@
     <div class="modal-content">
       <div class="modal-header">
         <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-        <h4 class="modal-title" id="about">Support</h4>
+        <h4 class="modal-title">Support</h4>
       </div>
       <div class="modal-body">
         <p></p>
@@ -425,32 +460,7 @@
 </div>
 <!-- END Support Modal -->
 
-<!-- START Connection Error Modal -->
-<div class="modal fade" id="connection-error" tabindex="-1" role="dialog" aria-labelledby="support" aria-hidden="true">
-  <div class="modal-dialog" role="document">
-    <div class="modal-content">
-      <div class="modal-header">
-        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-        <h4 class="modal-title" id="about">Off the Air</h4>
-      </div>
-      <div class="modal-body">
-         <div class="alert alert-danger" role="alert">
-            <p><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <b>Connection Error</b>: Unable to connect with <a href="http://stream.davidawindham.com/"><i>stream.davidawindham.com</i></a>.</p> 
-            <div class="progress" id="error-reconnecting">
-              <div class="progress-bar progress-bar-danger progress-bar-striped" id="connection-error-reconnecting" role="progressbar" aria-valuemin="0" aria-valuemax="100" data-transitiongoal="100">
-            </div>  
-         </div>
-         <p>I only play music when I'm listening to it live and the rest of the time the stream is offline. There is no reason to be here unless I am. Try again some other time, check my schedule, or <a href="https://davidawindham.com/contact">contact me</a>.</p>
-         </div>
-      </div>
-      <div class="modal-footer">
-         <button type="button" id="connection-error-retry" class="btn btn-danger">Retry</button>
-         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-      </div>
-    </div>
-  </div>
-</div>
-<!-- END Connection Error Modal -->
+
 
 <footer class="site-footer">
    

File diff suppressed because it is too large
+ 5 - 3141
src/js/amplitude-v2.2.0.js


+ 325 - 0
src/js/bootstrap-validator-v0.9.0.js

@@ -0,0 +1,325 @@
+/* ========================================================================
+ * Bootstrap (plugin): validator.js v0.9.0
+ * ========================================================================
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Cina Saffary.
+ * Made by @1000hz in the style of Bootstrap 3 era @fat
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ * ======================================================================== */
+
+
++function ($) {
+  'use strict';
+
+  // VALIDATOR CLASS DEFINITION
+  // ==========================
+
+  var Validator = function (element, options) {
+    this.$element = $(element)
+    this.options  = options
+
+    options.errors = $.extend({}, Validator.DEFAULTS.errors, options.errors)
+
+    for (var custom in options.custom) {
+      if (!options.errors[custom]) throw new Error('Missing default error message for custom validator: ' + custom)
+    }
+
+    $.extend(Validator.VALIDATORS, options.custom)
+
+    this.$element.attr('novalidate', true) // disable automatic native validation
+    this.toggleSubmit()
+
+    this.$element.on('input.bs.validator change.bs.validator focusout.bs.validator', $.proxy(this.validateInput, this))
+    this.$element.on('submit.bs.validator', $.proxy(this.onSubmit, this))
+
+    this.$element.find('[data-match]').each(function () {
+      var $this  = $(this)
+      var target = $this.data('match')
+
+      $(target).on('input.bs.validator', function (e) {
+        $this.val() && $this.trigger('input.bs.validator')
+      })
+    })
+  }
+
+  Validator.INPUT_SELECTOR = ':input:not([type="submit"], button):enabled:visible'
+
+  Validator.DEFAULTS = {
+    delay: 500,
+    html: false,
+    disable: true,
+    custom: {},
+    errors: {
+      match: 'Does not match',
+      minlength: 'Not long enough'
+    },
+    feedback: {
+      success: 'glyphicon-ok',
+      error: 'glyphicon-remove'
+    }
+  }
+
+  Validator.VALIDATORS = {
+    'native': function ($el) {
+      var el = $el[0]
+      return el.checkValidity ? el.checkValidity() : true
+    },
+    'match': function ($el) {
+      var target = $el.data('match')
+      return !$el.val() || $el.val() === $(target).val()
+    },
+    'minlength': function ($el) {
+      var minlength = $el.data('minlength')
+      return !$el.val() || $el.val().length >= minlength
+    }
+  }
+
+  Validator.prototype.validateInput = function (e) {
+    var $el        = $(e.target)
+    var prevErrors = $el.data('bs.validator.errors')
+    var errors
+
+    if ($el.is('[type="radio"]')) $el = this.$element.find('input[name="' + $el.attr('name') + '"]')
+
+    this.$element.trigger(e = $.Event('validate.bs.validator', {relatedTarget: $el[0]}))
+
+    if (e.isDefaultPrevented()) return
+
+    var self = this
+
+    this.runValidators($el).done(function (errors) {
+      $el.data('bs.validator.errors', errors)
+
+      errors.length ? self.showErrors($el) : self.clearErrors($el)
+
+      if (!prevErrors || errors.toString() !== prevErrors.toString()) {
+        e = errors.length
+          ? $.Event('invalid.bs.validator', {relatedTarget: $el[0], detail: errors})
+          : $.Event('valid.bs.validator', {relatedTarget: $el[0], detail: prevErrors})
+
+        self.$element.trigger(e)
+      }
+
+      self.toggleSubmit()
+
+      self.$element.trigger($.Event('validated.bs.validator', {relatedTarget: $el[0]}))
+    })
+  }
+
+
+  Validator.prototype.runValidators = function ($el) {
+    var errors   = []
+    var deferred = $.Deferred()
+    var options  = this.options
+
+    $el.data('bs.validator.deferred') && $el.data('bs.validator.deferred').reject()
+    $el.data('bs.validator.deferred', deferred)
+
+    function getErrorMessage(key) {
+      return $el.data(key + '-error')
+        || $el.data('error')
+        || key == 'native' && $el[0].validationMessage
+        || options.errors[key]
+    }
+
+    $.each(Validator.VALIDATORS, $.proxy(function (key, validator) {
+      if (($el.data(key) || key == 'native') && !validator.call(this, $el)) {
+        var error = getErrorMessage(key)
+        !~errors.indexOf(error) && errors.push(error)
+      }
+    }, this))
+
+    if (!errors.length && $el.val() && $el.data('remote')) {
+      this.defer($el, function () {
+        var data = {}
+        data[$el.attr('name')] = $el.val()
+        $.get($el.data('remote'), data)
+          .fail(function (jqXHR, textStatus, error) { errors.push(getErrorMessage('remote') || error) })
+          .always(function () { deferred.resolve(errors)})
+      })
+    } else deferred.resolve(errors)
+
+    return deferred.promise()
+  }
+
+  Validator.prototype.validate = function () {
+    var delay = this.options.delay
+
+    this.options.delay = 0
+    this.$element.find(Validator.INPUT_SELECTOR).trigger('input.bs.validator')
+    this.options.delay = delay
+
+    return this
+  }
+
+  Validator.prototype.showErrors = function ($el) {
+    var method = this.options.html ? 'html' : 'text'
+
+    this.defer($el, function () {
+      var $group = $el.closest('.form-group')
+      var $block = $group.find('.help-block.with-errors')
+      var $feedback = $group.find('.form-control-feedback')
+      var errors = $el.data('bs.validator.errors')
+
+      if (!errors.length) return
+
+      errors = $('<ul/>')
+        .addClass('list-unstyled')
+        .append($.map(errors, function (error) { return $('<li/>')[method](error) }))
+
+      $block.data('bs.validator.originalContent') === undefined && $block.data('bs.validator.originalContent', $block.html())
+      $block.empty().append(errors)
+      $group.addClass('has-error')
+
+      $feedback.length
+        && $feedback.removeClass(this.options.feedback.success)
+        && $feedback.addClass(this.options.feedback.error)
+        && $group.removeClass('has-success')
+    })
+  }
+
+  Validator.prototype.clearErrors = function ($el) {
+    var $group = $el.closest('.form-group')
+    var $block = $group.find('.help-block.with-errors')
+    var $feedback = $group.find('.form-control-feedback')
+
+    $block.html($block.data('bs.validator.originalContent'))
+    $group.removeClass('has-error')
+
+    $feedback.length
+      && $feedback.removeClass(this.options.feedback.error)
+      && $feedback.addClass(this.options.feedback.success)
+      && $group.addClass('has-success')
+  }
+
+  Validator.prototype.hasErrors = function () {
+    function fieldErrors() {
+      return !!($(this).data('bs.validator.errors') || []).length
+    }
+
+    return !!this.$element.find(Validator.INPUT_SELECTOR).filter(fieldErrors).length
+  }
+
+  Validator.prototype.isIncomplete = function () {
+    function fieldIncomplete() {
+      return this.type === 'checkbox' ? !this.checked                                   :
+             this.type === 'radio'    ? !$('[name="' + this.name + '"]:checked').length :
+                                        $.trim(this.value) === ''
+    }
+
+    return !!this.$element.find(Validator.INPUT_SELECTOR).filter('[required]').filter(fieldIncomplete).length
+  }
+
+  Validator.prototype.onSubmit = function (e) {
+    this.validate()
+    if (this.isIncomplete() || this.hasErrors()) e.preventDefault()
+  }
+
+  Validator.prototype.toggleSubmit = function () {
+    if(!this.options.disable) return
+
+    var $btn = $('button[type="submit"], input[type="submit"]')
+      .filter('[form="' + this.$element.attr('id') + '"]')
+      .add(this.$element.find('input[type="submit"], button[type="submit"]'))
+
+    $btn.toggleClass('disabled', this.isIncomplete() || this.hasErrors())
+  }
+
+  Validator.prototype.defer = function ($el, callback) {
+    callback = $.proxy(callback, this)
+    if (!this.options.delay) return callback()
+    window.clearTimeout($el.data('bs.validator.timeout'))
+    $el.data('bs.validator.timeout', window.setTimeout(callback, this.options.delay))
+  }
+
+  Validator.prototype.destroy = function () {
+    this.$element
+      .removeAttr('novalidate')
+      .removeData('bs.validator')
+      .off('.bs.validator')
+
+    this.$element.find(Validator.INPUT_SELECTOR)
+      .off('.bs.validator')
+      .removeData(['bs.validator.errors', 'bs.validator.deferred'])
+      .each(function () {
+        var $this = $(this)
+        var timeout = $this.data('bs.validator.timeout')
+        window.clearTimeout(timeout) && $this.removeData('bs.validator.timeout')
+      })
+
+    this.$element.find('.help-block.with-errors').each(function () {
+      var $this = $(this)
+      var originalContent = $this.data('bs.validator.originalContent')
+
+      $this
+        .removeData('bs.validator.originalContent')
+        .html(originalContent)
+    })
+
+    this.$element.find('input[type="submit"], button[type="submit"]').removeClass('disabled')
+
+    this.$element.find('.has-error').removeClass('has-error')
+
+    return this
+  }
+
+  // VALIDATOR PLUGIN DEFINITION
+  // ===========================
+
+
+  function Plugin(option) {
+    return this.each(function () {
+      var $this   = $(this)
+      var options = $.extend({}, Validator.DEFAULTS, $this.data(), typeof option == 'object' && option)
+      var data    = $this.data('bs.validator')
+
+      if (!data && option == 'destroy') return
+      if (!data) $this.data('bs.validator', (data = new Validator(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  var old = $.fn.validator
+
+  $.fn.validator             = Plugin
+  $.fn.validator.Constructor = Validator
+
+
+  // VALIDATOR NO CONFLICT
+  // =====================
+
+  $.fn.validator.noConflict = function () {
+    $.fn.validator = old
+    return this
+  }
+
+
+  // VALIDATOR DATA-API
+  // ==================
+
+  $(window).on('load', function () {
+    $('form[data-toggle="validator"]').each(function () {
+      var $form = $(this)
+      Plugin.call($form, $form.data())
+    })
+  })
+
+}(jQuery);

+ 0 - 3
src/js/chat.js

@@ -300,9 +300,6 @@
     $('#b_set_nickname').click(function(eventObject) {
         eventObject.preventDefault();
         socket.emit('setNickname', {'username':getNickname()});
-
-        // Close modal if opened
-        $('#modal_setnick').modal('hide');
     });
 
 })();

+ 38 - 62
src/js/radio.js

@@ -1,8 +1,13 @@
+/*global amplitude_config:true */
+
+
+/* Radio Funtions */
+
 /**** Last-fm API ****/
 
 (function( $ ) {
   
-	$.fn.lfya = function(options){
+	$.fn.lfya = function(){
 		var urla = "https://ws.audioscrobbler.com/2.0/?method=user.gettopartists&user=windhamdavid&api_key=e12ea1d0253898ee9a93edfe42ffdeab&period=12month&format=json&limit=200";
 		var artists = [];
 		function isLoadeda (artistElement) {
@@ -27,7 +32,7 @@
   
   $('.artists').lfya();
   
-	$.fn.lfyt = function(options){
+	$.fn.lfyt = function(){
 		var urla = "https://ws.audioscrobbler.com/2.0/?method=user.gettoptracks&user=windhamdavid&api_key=e12ea1d0253898ee9a93edfe42ffdeab&period=12month&format=json&limit=200";
 		var tracks = [];
 		function isLoadedt (tracksElement) {
@@ -53,7 +58,7 @@
 	
 	$('.tracks').lfyt();
   
-	$.fn.lfm = function(options){
+	$.fn.lfm = function(){
 		var url = "https://ws.audioscrobbler.com/2.0/?method=user.gettopalbums&user=windhamdavid&api_key=e12ea1d0253898ee9a93edfe42ffdeab&period=12month&format=json&limit=200";
 		var albums = [];
 		function isLoaded (albumElement) {
@@ -88,11 +93,11 @@
 })( jQuery );
 
 
-getRecentTracks();  //call it once to avoid delay
+
 
 function getRecentTracks() {
   
-	$.fn.lfmr = function(options){
+	$.fn.lfmr = function(){
     //tracksinterval = setInterval(getRecentTracks,180000);  // check every 3 minutes
 		var urla = "https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=windhamdavid&api_key=e12ea1d0253898ee9a93edfe42ffdeab&format=json&limit=100";
 		var tracks = [];
@@ -120,56 +125,26 @@ function getRecentTracks() {
 	$('.recent').lfmr();
   $('.recent').hide();
   $('.recent').fadeIn(1000);
-}( jQuery );
-
-
+}
 
 
 /**** Audio Player ****/
 
 amplitude_config = {
-//  amplitude_songs: []
 	"amplitude_songs": [{
 			"url": "http://stream.davidawindham.com:8008/stream",
 			"live": true
 		}],
 	"amplitude_volume": 73
-}
+};
 
 function get_radio_tower() {return 'img/radio.gif';}
 function get_radio_none() {return 'img/none.svg';}
 function get_radio_eq() {return 'img/eq.gif';}
 function get_radio_eq_none() {return 'img/1.png';}
 
-var interval = null;
 
-$(document).ready(function() {
-  interval = setInterval(radioTitle,20000); // every 20 seconds or stop polling
-  $('#error-reconnecting').hide();
-  $('#connection-error-retry').on('click', function () {
-      radioTitle();
-      $('#error-reconnecting').show();
-      $('#connection-error-reconnecting').attr('data-transitiongoal', 100).progressbar();
-    })
-});
-
-$('#amplitude-play-pause').click(function() {
-  radioTitle();
-});
-
-$('#connection-error-retry').click(function() {
-  setTimeout(function () {
-      $('#connection-error').modal('hide');
-  }, 2000);
-  setTimeout(function () {
-      radioTitle();
-      $('#connection-error-reconnecting').attr('data-transitiongoal', 0).progressbar();
-      $('#error-reconnecting').hide();
-      
-  }, 3000);
-});
-
-//radioTitle(); // call it once on load to avoid 20s delay
+var interval = null;
 
 function radioTitle() {
     var url = 'http://stream.davidawindham.com/status2.xsl';
@@ -183,47 +158,48 @@ function radioTitle() {
       success: function(json){
         if(json[mountpoint] == null) {
           $('#connection-error').modal('show');
-        	$('#radio').attr('src', get_radio_none()).fadeIn(300);
-        	$('#eq').attr('src', get_radio_eq_none()).fadeIn(300);
-          clearInterval(interval);
+          $('#error-reconnecting').hide();
+          $('#error-reconnecting-again').hide();
+          $('#connection-error-reconnecting').attr('data-transitiongoal', 0).progressbar();
+          $('#connection-error-retry').on('click', function () {
+            $('#error-reconnecting').show();
+            $('#connection-error-reconnecting').attr('data-transitiongoal', 100).progressbar({
+                done: function() { $('#error-reconnecting-again').show(); }
+            });
+          });
+          $('#radio').attr('src', get_radio_none()).fadeIn(300);
+          $('#eq').attr('src', get_radio_eq_none()).fadeIn(300);
         }
         else {
+          $('#connection-error').modal('hide');
           $('#track').text(json[mountpoint].title);
           $('#listeners').text(json[mountpoint].listeners);
           $('#peak-listeners').text(json[mountpoint].peak_listeners);
           $('#bitrate').text(json[mountpoint].bitrate);
           $('#radio').attr('src', get_radio_tower()).fadeIn(300);
           $('#eq').attr('src', get_radio_eq()).fadeIn(300);
-          $('#connection-error').modal('hide');
-        }		
+        }
       },
-      error: function(e){
+      error: function(){
         $('#connection-error').modal('show');
         clearInterval(interval);
-  		  $('#radio').attr('src', get_radio_none()).fadeIn(300);
-  		  $('#eq').attr('src', get_radio_eq_none()).fadeIn(300);
+        $('#radio').attr('src', get_radio_none()).fadeIn(300);
+        $('#eq').attr('src', get_radio_eq_none()).fadeIn(300);
       }
   });
 }
-
-
-
-
-
+interval = setInterval(radioTitle,20000); // every 20 seconds or stop polling
 
 /**** Page Features ****/
 
 $(document).ready(function() {
-  function spectrum() {
-      var randomColor = Math.floor(Math.random()*16777215).toString(16);
-      $("span#user-label").css({ backgroundColor: '#' + randomColor });
-  };                        
+  $('#auth-modal').modal('show');
+  $('#nickname').validator();
+
+  radioTitle(); // call it once on load to avoid 20s delay
+  getRecentTracks();  //call it once to avoid 3m delay
+  var randomColor = Math.floor(Math.random()*16777215).toString(16);
+  $("span#user-label").css({ backgroundColor: '#' + randomColor });
   $('ul.nav-tabs a').tooltip();
   
-});
-
-
-
-
-
-
+});