# HG changeset patch
# User Anton Shestakov <av6@dwimlabs.net>
# Date 1459048882 -28800
# Node ID e3696f4866fc4fdeac7e6ff1e0a7cd92a13e766e
# Parent  3e09cda9a6607945973690a775f4c00732beb18c
register: redo form validation using rivets

diff -r 3e09cda9a660 -r e3696f4866fc coffee/register.coffee
--- a/coffee/register.coffee	Fri Mar 25 10:16:40 2016 +0800
+++ b/coffee/register.coffee	Sun Mar 27 11:21:22 2016 +0800
@@ -1,3 +1,69 @@
+class RegistrationData extends Backbone.Model
+  defaults:
+    username: ''
+    password1: ''
+    password2: ''
+
+  validate: (attrs, options) ->
+    @unset('username-errors')
+    @unset('password1-errors')
+    @unset('password2-errors')
+    if (attrs.username ? '').trim() is ''
+      @set('username-errors', ['This field is required.'])
+    if (attrs.password1 ? '') is ''
+      @set('password1-errors', ['This field is required.'])
+    if (attrs.password1 ? '') isnt (attrs.password2 ? '')
+      @set('password2-errors', ['Passwords must match.'])
+    else if (attrs.password2 ? '') is ''
+      @set('password2-errors', ['This field is required.'])
+    return @has('username-errors') or @has('password1-errors') or @has('password2-errors')
+
+
+class ProfileData extends Backbone.Model
+  defaults:
+    fullname: ''
+    nickname: ''
+    avatar: ''
+
+
+window.regData = new RegistrationData()
+window.vcData = new ProfileData()
+
+
+registerfn = ->
+  if regData.isValid()
+    X.register(regData.get('username').trim(), regData.get('password1'))
+
+
+savefn = ->
+  data =
+    fullname: vcData.get('fullname').trim()
+    nickname: vcData.get('nickname').trim()
+
+  okcb = ->
+    console.debug('vcard is set!', arguments)
+    $('[data-step="vcard"]').addClass('uk-hidden')
+    location.href = '/'
+
+  failcb = ->
+    console.warn('vcard is NOT set!', arguments)
+    $('[data-msg="save-failed"]').removeClass('uk-hidden')
+
+  file = $('#avatar').get(0).files[0]
+  if file?
+    reader = new FileReader()
+    reader.onloadend = ->
+      _(data).extend(avatar: reader.result)
+      X.savevCard(data, okcb, failcb)
+    reader.readAsDataURL(file)
+  else
+    X.savevCard(data, okcb, failcb)
+
+
+window.regRivet = rivets.bind($('[data-form="registration"]'), data: regData, register: registerfn)
+window.vcRivet = rivets.bind($('[data-form="vcard"]'), data: vcData, save: savefn)
+
+
 window.X = new Tram.XMPPInterface()
 
 
@@ -15,7 +81,7 @@
 
 
 X.on 'conflict', ->
-  Tram.validation.setError($('#username'), 'A user with this username already exists.')
+  regData.set('username-errors', ['A user with this username already exists.'])
 
 
 X.on 'notacceptable', ->
@@ -23,7 +89,7 @@
 
 
 X.on 'connected', ->
-  $('#nickname').val($('#username').val())
+  vcData.set('nickname', regData.get('username').trim())
   $('[data-step="vcard"]').removeClass('uk-hidden')
   $('#fullname').focus()
 
@@ -42,39 +108,3 @@
         $next.focus()
       else
         $form.find('button').trigger('click')
-
-
-$('[data-form="registration"] button').on 'click', ->
-  ok = true
-  Tram.validation.unsetError($('#username, #password1, #password2'))
-  ok &= Tram.validation.validateRequired($('#username'), true)
-  ok &= Tram.validation.validatePasswords($('#password1'), $('#password2'))
-  ok &= Tram.validation.validateRequired($('#password1'))
-  ok &= Tram.validation.validateRequired($('#password2'))
-  if ok
-    X.register($('#username').val().trim(), $('#password1').val())
-
-
-$('[data-form="vcard"] button').on 'click', ->
-  data =
-    fullname: $('#fullname').val()
-    nickname: $('#nickname').val()
-
-  okcb = ->
-    console.debug('vcard is set!', arguments)
-    $('[data-step="vcard"]').addClass('uk-hidden')
-    location.href = '/'
-
-  failcb = ->
-    console.warn('vcard is NOT set!', arguments)
-    $('[data-msg="save-failed"]').removeClass('uk-hidden')
-
-  file = $('#avatar').get(0).files[0]
-  if file
-    reader = new FileReader()
-    reader.onloadend = ->
-      _(data).extend(avatar: reader.result)
-      X.savevCard(data, okcb, failcb)
-    reader.readAsDataURL(file)
-  else
-    X.savevCard(data, okcb, failcb)
diff -r 3e09cda9a660 -r e3696f4866fc js/register.js
--- a/js/register.js	Fri Mar 25 10:16:40 2016 +0800
+++ b/js/register.js	Sun Mar 27 11:21:22 2016 +0800
@@ -1,5 +1,112 @@
 // Generated by CoffeeScript 1.10.0
 (function() {
+  var ProfileData, RegistrationData, registerfn, savefn,
+    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+    hasProp = {}.hasOwnProperty;
+
+  RegistrationData = (function(superClass) {
+    extend(RegistrationData, superClass);
+
+    function RegistrationData() {
+      return RegistrationData.__super__.constructor.apply(this, arguments);
+    }
+
+    RegistrationData.prototype.defaults = {
+      username: '',
+      password1: '',
+      password2: ''
+    };
+
+    RegistrationData.prototype.validate = function(attrs, options) {
+      var ref, ref1, ref2, ref3, ref4;
+      this.unset('username-errors');
+      this.unset('password1-errors');
+      this.unset('password2-errors');
+      if (((ref = attrs.username) != null ? ref : '').trim() === '') {
+        this.set('username-errors', ['This field is required.']);
+      }
+      if (((ref1 = attrs.password1) != null ? ref1 : '') === '') {
+        this.set('password1-errors', ['This field is required.']);
+      }
+      if (((ref2 = attrs.password1) != null ? ref2 : '') !== ((ref3 = attrs.password2) != null ? ref3 : '')) {
+        this.set('password2-errors', ['Passwords must match.']);
+      } else if (((ref4 = attrs.password2) != null ? ref4 : '') === '') {
+        this.set('password2-errors', ['This field is required.']);
+      }
+      return this.has('username-errors') || this.has('password1-errors') || this.has('password2-errors');
+    };
+
+    return RegistrationData;
+
+  })(Backbone.Model);
+
+  ProfileData = (function(superClass) {
+    extend(ProfileData, superClass);
+
+    function ProfileData() {
+      return ProfileData.__super__.constructor.apply(this, arguments);
+    }
+
+    ProfileData.prototype.defaults = {
+      fullname: '',
+      nickname: '',
+      avatar: ''
+    };
+
+    return ProfileData;
+
+  })(Backbone.Model);
+
+  window.regData = new RegistrationData();
+
+  window.vcData = new ProfileData();
+
+  registerfn = function() {
+    if (regData.isValid()) {
+      return X.register(regData.get('username').trim(), regData.get('password1'));
+    }
+  };
+
+  savefn = function() {
+    var data, failcb, file, okcb, reader;
+    data = {
+      fullname: vcData.get('fullname').trim(),
+      nickname: vcData.get('nickname').trim()
+    };
+    okcb = function() {
+      console.debug('vcard is set!', arguments);
+      $('[data-step="vcard"]').addClass('uk-hidden');
+      return location.href = '/';
+    };
+    failcb = function() {
+      console.warn('vcard is NOT set!', arguments);
+      return $('[data-msg="save-failed"]').removeClass('uk-hidden');
+    };
+    file = $('#avatar').get(0).files[0];
+    if (file != null) {
+      reader = new FileReader();
+      reader.onloadend = function() {
+        _(data).extend({
+          avatar: reader.result
+        });
+        return X.savevCard(data, okcb, failcb);
+      };
+      return reader.readAsDataURL(file);
+    } else {
+      return X.savevCard(data, okcb, failcb);
+    }
+  };
+
+  window.regRivet = rivets.bind($('[data-form="registration"]'), {
+    data: regData,
+    register: registerfn
+  });
+
+  window.vcRivet = rivets.bind($('[data-form="vcard"]'), {
+    data: vcData,
+    save: savefn
+  });
+
   window.X = new Tram.XMPPInterface();
 
   X.on('register', function() {
@@ -16,7 +123,7 @@
   });
 
   X.on('conflict', function() {
-    return Tram.validation.setError($('#username'), 'A user with this username already exists.');
+    return regData.set('username-errors', ['A user with this username already exists.']);
   });
 
   X.on('notacceptable', function() {
@@ -24,7 +131,7 @@
   });
 
   X.on('connected', function() {
-    $('#nickname').val($('#username').val());
+    vcData.set('nickname', regData.get('username').trim());
     $('[data-step="vcard"]').removeClass('uk-hidden');
     return $('#fullname').focus();
   });
@@ -49,49 +156,6 @@
     });
   });
 
-  $('[data-form="registration"] button').on('click', function() {
-    var ok;
-    ok = true;
-    Tram.validation.unsetError($('#username, #password1, #password2'));
-    ok &= Tram.validation.validateRequired($('#username'), true);
-    ok &= Tram.validation.validatePasswords($('#password1'), $('#password2'));
-    ok &= Tram.validation.validateRequired($('#password1'));
-    ok &= Tram.validation.validateRequired($('#password2'));
-    if (ok) {
-      return X.register($('#username').val().trim(), $('#password1').val());
-    }
-  });
-
-  $('[data-form="vcard"] button').on('click', function() {
-    var data, failcb, file, okcb, reader;
-    data = {
-      fullname: $('#fullname').val(),
-      nickname: $('#nickname').val()
-    };
-    okcb = function() {
-      console.debug('vcard is set!', arguments);
-      $('[data-step="vcard"]').addClass('uk-hidden');
-      return location.href = '/';
-    };
-    failcb = function() {
-      console.warn('vcard is NOT set!', arguments);
-      return $('[data-msg="save-failed"]').removeClass('uk-hidden');
-    };
-    file = $('#avatar').get(0).files[0];
-    if (file) {
-      reader = new FileReader();
-      reader.onloadend = function() {
-        _(data).extend({
-          avatar: reader.result
-        });
-        return X.savevCard(data, okcb, failcb);
-      };
-      return reader.readAsDataURL(file);
-    } else {
-      return X.savevCard(data, okcb, failcb);
-    }
-  });
-
 }).call(this);
 
 //# sourceMappingURL=register.js.map
diff -r 3e09cda9a660 -r e3696f4866fc js/register.js.map
--- a/js/register.js.map	Fri Mar 25 10:16:40 2016 +0800
+++ b/js/register.js.map	Sun Mar 27 11:21:22 2016 +0800
@@ -6,5 +6,5 @@
     "coffee/register.coffee"
   ],
   "names": [],
-  "mappings": "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"
+  "mappings": "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"
 }
\ No newline at end of file
diff -r 3e09cda9a660 -r e3696f4866fc register.html
--- a/register.html	Fri Mar 25 10:16:40 2016 +0800
+++ b/register.html	Sun Mar 27 11:21:22 2016 +0800
@@ -10,6 +10,7 @@
     <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.3/backbone-min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/rivets/0.8.1/rivets.bundled.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.25.0/js/uikit.min.js"></script>
     <script src="/vendor/strophejs/strophe.min.js"></script>
     <script src="/vendor/strophejs-plugins/register/strophe.register.js"></script>
@@ -32,25 +33,31 @@
         <div class="uk-form-row">
           <label for="username" class="uk-form-label">Username</label>
           <div class="uk-form-controls">
-            <input id="username" type="text" class="uk-width-1-1" value="" required autofocus>
+            <input id="username" type="text" class="uk-width-1-1" required autofocus
+                   rv-value="data:username" rv-class-uk-form-danger="data:username-errors">
+            <div class="uk-text-danger" rv-each-error="data:username-errors">{ error }</div>
           </div>
         </div>
         <div class="uk-form-row">
           <label for="password1" class="uk-form-label">Password</label>
           <div class="uk-form-controls">
-            <input id="password1" type="password" class="uk-width-1-1" value="" required>
+            <input id="password1" type="password" class="uk-width-1-1" required
+                   rv-value="data:password1" rv-class-uk-form-danger="data:password1-errors">
+            <div class="uk-text-danger" rv-each-error="data:password1-errors">{ error }</div>
           </div>
         </div>
         <div class="uk-form-row">
           <label for="password2" class="uk-form-label">Confirmation</label>
           <div class="uk-form-controls">
-            <input id="password2" type="password" class="uk-width-1-1" value="" required>
+            <input id="password2" type="password" class="uk-width-1-1" required
+                   rv-value="data:password2" rv-class-uk-form-danger="data:password2-errors">
+            <div class="uk-text-danger" rv-each-error="data:password2-errors">{ error }</div>
           </div>
         </div>
         <div class="uk-form-row">
           <div class="uk-form-controls">
             <div class="uk-alert uk-alert-danger uk-hidden" data-msg="registration-failed">Unspecified error, sorry.</div>
-            <button type="button" class="uk-button uk-button-primary">Register</button>
+            <button type="button" class="uk-button uk-button-primary" rv-on-click="register">Register</button>
             <a class="uk-float-right uk-button uk-button-link" href="/">Log in</a>
           </div>
         </div>
@@ -60,13 +67,13 @@
         <div class="uk-form-row">
           <label for="fullname" class="uk-form-label">Full Name</label>
           <div class="uk-form-controls">
-            <input id="fullname" class="uk-width-1-1" type="text" value="">
+            <input id="fullname" class="uk-width-1-1" type="text" rv-value="data:fullname">
           </div>
         </div>
         <div class="uk-form-row">
           <label for="nickname" class="uk-form-label">Nickname</label>
           <div class="uk-form-controls">
-            <input id="nickname" class="uk-width-1-1" type="text" value="">
+            <input id="nickname" class="uk-width-1-1" type="text" rv-value="data:nickname">
           </div>
         </div>
         <div class="uk-form-row">
@@ -77,7 +84,7 @@
         </div>
         <div class="uk-form-row">
           <div class="uk-form-controls">
-            <button class="uk-button uk-button-primary">Save</button>
+            <button class="uk-button uk-button-primary" rv-on-click="save">Save</button>
           </div>
         </div>
       </div>
@@ -86,7 +93,7 @@
     </div>
 
     <script src='/js/tram.js'></script>
-    <script src='/js/validation.js'></script>
+    <script src='/js/rivets.js'></script>
     <script src='/js/xmpp.js'></script>
     <script src='/config.js'></script>
     <script src='/js/register.js'></script>