about to be able to create user
This commit is contained in:
		@@ -41,9 +41,15 @@ function SafeChat() {
 | 
			
		||||
 | 
			
		||||
  /// Create UID from a name by appending an E-Mail
 | 
			
		||||
  function uid(name) {
 | 
			
		||||
    return name+' <'+name+'@'+hostname+'>'
 | 
			
		||||
    return name+' <'+mail(name)+'>'
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function mail(name) {
 | 
			
		||||
    var hostname = window.location.hostname!='localhost'?window.location.hostname:'safechat.ch'
 | 
			
		||||
    return name+'@'+hostname
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  //==============================================================================
 | 
			
		||||
  /// @class Crypto cryptographic functions
 | 
			
		||||
  /** @param view is of class SafeChat.View */
 | 
			
		||||
  function Crypto(controller) {
 | 
			
		||||
@@ -51,42 +57,23 @@ function SafeChat() {
 | 
			
		||||
    /// cache client's key from local strorage
 | 
			
		||||
    var k = null
 | 
			
		||||
 | 
			
		||||
    /// detect hosstname, default to safechat.ch
 | 
			
		||||
    var hostname = window.location.hostname!='localhost'?window.location.hostname:'safechat.ch'
 | 
			
		||||
    
 | 
			
		||||
    /// detect hostname, default to safechat.ch
 | 
			
		||||
    /// get user key
 | 
			
		||||
    /** @internal key ist cached in k
 | 
			
		||||
       @return key */
 | 
			
		||||
    function key() {
 | 
			
		||||
      if (k) return k
 | 
			
		||||
      if (typeof localStorage.key == 'undefined') return null
 | 
			
		||||
      return k = openpgp.key.readArmored(localStorage.key)
 | 
			
		||||
    this.key = function() {
 | 
			
		||||
      if (k) return k // cached key
 | 
			
		||||
      if (typeof localStorage.privkey === 'undefined') return null
 | 
			
		||||
      return k = openpgp.key.readArmored(localStorage.privkey)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// get own user name
 | 
			
		||||
    /** get user name as user id of first public key */
 | 
			
		||||
    function user() {
 | 
			
		||||
    this.user = function() {
 | 
			
		||||
      if (k || key()) return k.pub.keys[0].getUserIds()[0]
 | 
			
		||||
      return null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// create New User
 | 
			
		||||
    function createuser(user, email, pwd) {
 | 
			
		||||
      controller.notice("generating keys")
 | 
			
		||||
      openpgp.generateKey({
 | 
			
		||||
        numBits: 4096,
 | 
			
		||||
        userIds: [{name: user, email: email}],
 | 
			
		||||
        passphrase: pwd
 | 
			
		||||
      }).then(function(keyPair) {
 | 
			
		||||
        controller.success("keys generated")
 | 
			
		||||
        localStorage.key = keyPair.privateKeyArmored
 | 
			
		||||
        k = keyPair.key
 | 
			
		||||
      }).catch(function(e) {
 | 
			
		||||
        console.log(e)
 | 
			
		||||
        controller.fatal("generating key pairs failed")
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// open private key with password
 | 
			
		||||
    /** @return @c true if password matches */
 | 
			
		||||
    function password(pwd) {
 | 
			
		||||
@@ -116,8 +103,15 @@ function SafeChat() {
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //------------------------------------------------------------------------------
 | 
			
		||||
    if (openpgp.initWorker("openpgp.worker.min.js"))
 | 
			
		||||
      console.log("asynchronous openpgp enabled")
 | 
			
		||||
    else
 | 
			
		||||
      console.log("asynchronous openpgp failed")
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //==============================================================================
 | 
			
		||||
  /// database that stores in indexed db
 | 
			
		||||
  function DataBase() {
 | 
			
		||||
 | 
			
		||||
@@ -126,6 +120,7 @@ function SafeChat() {
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //==============================================================================
 | 
			
		||||
  /// manage local copy of users
 | 
			
		||||
  function Users() {
 | 
			
		||||
    
 | 
			
		||||
@@ -142,6 +137,7 @@ function SafeChat() {
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //------------------------------------------------------------------------------
 | 
			
		||||
  /// manage local copy of messages
 | 
			
		||||
  function Messages() {
 | 
			
		||||
    
 | 
			
		||||
@@ -152,6 +148,7 @@ function SafeChat() {
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //==============================================================================
 | 
			
		||||
  /// @class Communication client socket communication
 | 
			
		||||
  /** @param view is of class SafeChat.View */
 | 
			
		||||
  function Communication(controller) {
 | 
			
		||||
@@ -163,9 +160,13 @@ function SafeChat() {
 | 
			
		||||
      socket.broadcast.emit(signal, data)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function emit(signal, data) {
 | 
			
		||||
    function emit(signal, data, next) {
 | 
			
		||||
      console.log("<-snd "+signal)
 | 
			
		||||
      socket.emit(signal, data)
 | 
			
		||||
      socket.emit(signal, data, next)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.lookup = function(usr, next) {
 | 
			
		||||
      emit('user', usr, next)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
@@ -183,6 +184,7 @@ function SafeChat() {
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //==============================================================================
 | 
			
		||||
  /// @class View provides the glue to the GUI in the index.ejs file
 | 
			
		||||
  /** View provides the following callbacks:
 | 
			
		||||
     - status updates:
 | 
			
		||||
@@ -299,7 +301,7 @@ function SafeChat() {
 | 
			
		||||
        @param msg (optional) the success message text */
 | 
			
		||||
    function show(id, msg) {
 | 
			
		||||
      console.log("state: "+id)
 | 
			
		||||
      if (msg) success(msg) else $("#status").hide()
 | 
			
		||||
      if (msg) success(msg); else $("#status").hide();
 | 
			
		||||
      $("#main").children(":not(#"+id+")").hide()
 | 
			
		||||
      $("#main #"+id).show()
 | 
			
		||||
      $("#main #"+id+" form input:first-child").focus()
 | 
			
		||||
@@ -327,21 +329,15 @@ function SafeChat() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function checkFeature(id, query) {
 | 
			
		||||
      if (query) $('#'+id+':before')
 | 
			
		||||
        .css('color', 'green')
 | 
			
		||||
        .css('content', '✔')
 | 
			
		||||
      else $('#'+id+':before')
 | 
			
		||||
        .css('color', 'red')
 | 
			
		||||
        .css('content', '✘')
 | 
			
		||||
      if (query) $('#'+id)
 | 
			
		||||
        .css('color', 'green')
 | 
			
		||||
        .css('text-decoration', 'line-through')
 | 
			
		||||
        .prepend('<span>✔</span>')
 | 
			
		||||
      else $('#'+id)
 | 
			
		||||
        .css('color', 'red')
 | 
			
		||||
        .css('text-decoration', 'none')
 | 
			
		||||
        .prepend('<span>✘</span>')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function checkFeatures() {
 | 
			
		||||
    this.checkFeatures = function() {
 | 
			
		||||
      $('ul.features').css('list-style-type', 'none')
 | 
			
		||||
      checkFeature("localstorage", Storage)
 | 
			
		||||
      checkFeature("indexeddb", window.indexedDB)
 | 
			
		||||
@@ -350,6 +346,54 @@ function SafeChat() {
 | 
			
		||||
      checkFeature("filereader", window.FileReader)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @name create new user
 | 
			
		||||
    /// @{
 | 
			
		||||
    
 | 
			
		||||
    this.newuser = function() {
 | 
			
		||||
      show('newuser')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var user = null
 | 
			
		||||
    var pwd = false
 | 
			
		||||
 | 
			
		||||
    function invalid(usr) {
 | 
			
		||||
      return !user || !user.exists && user.name.length<3
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    this.available = function(usr) {
 | 
			
		||||
      user = usr
 | 
			
		||||
      console.log("props:", invalid(user) || !pwd)
 | 
			
		||||
      $("#createuser").prop(":disabled", invalid(user) || !pwd)
 | 
			
		||||
      if (user.length==0)
 | 
			
		||||
        notice("please chose a user name")
 | 
			
		||||
      else if (user.length<3)
 | 
			
		||||
        notice("please chose a longer user name")
 | 
			
		||||
      else if (user.exists)
 | 
			
		||||
        notice("user name is already in use")
 | 
			
		||||
      else if (!pwd)
 | 
			
		||||
        notice("please chose a password")
 | 
			
		||||
      else
 | 
			
		||||
        success("user is ready to be created")
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    this.passwords = function(pwd1, pwd2) {
 | 
			
		||||
      pwd = pwd1==pwd2 && pwd1.length>5
 | 
			
		||||
      console.log("props:", invalid(user) || !pwd)
 | 
			
		||||
      $("#createuser").prop(":disabled", invalid(user) || !pwd)
 | 
			
		||||
      if (pwd1.length==0)
 | 
			
		||||
        notice('please chose a password')
 | 
			
		||||
      else if (pwd1.length<6)
 | 
			
		||||
        notice('please chose a longer password')
 | 
			
		||||
      else if (pwd1 != pwd2)
 | 
			
		||||
        notice("passwords don't match")
 | 
			
		||||
      else if (invalid(user))
 | 
			
		||||
        notice("please chose a user name")
 | 
			
		||||
      else
 | 
			
		||||
        success("user is ready to be created")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @}
 | 
			
		||||
 | 
			
		||||
    function DataTransfer() {
 | 
			
		||||
      
 | 
			
		||||
      var reboottimer = null
 | 
			
		||||
@@ -405,10 +449,11 @@ function SafeChat() {
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //==============================================================================
 | 
			
		||||
  /// @class Controller defines the programm flow
 | 
			
		||||
  function Controller(view) {
 | 
			
		||||
    
 | 
			
		||||
    var db = new Database()
 | 
			
		||||
    var db = new DataBase()
 | 
			
		||||
    var crypto = new Crypto(this)
 | 
			
		||||
    var communication = new Communication(this)
 | 
			
		||||
    var users = new Users()
 | 
			
		||||
@@ -461,6 +506,33 @@ function SafeChat() {
 | 
			
		||||
 | 
			
		||||
    // @}
 | 
			
		||||
 | 
			
		||||
    /// @name signals from view
 | 
			
		||||
    /// @{
 | 
			
		||||
 | 
			
		||||
    /// @name new user registration
 | 
			
		||||
    /// @{
 | 
			
		||||
 | 
			
		||||
    this.lookup = function(usr) {
 | 
			
		||||
      if (usr.length > 2) communication.lookup(uid(usr), function(res) {
 | 
			
		||||
        view.available(res)
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    this.checkpasswords = view.passwords
 | 
			
		||||
 | 
			
		||||
    this.createuser = function(name, pwd) {
 | 
			
		||||
      crypto.createuser(name, name+'@'+hostname, pwd).then(function() {
 | 
			
		||||
        if (!crypto.password(pwd))
 | 
			
		||||
          fatal("private key decryption failed")
 | 
			
		||||
        else
 | 
			
		||||
          chat()
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// @}
 | 
			
		||||
 | 
			
		||||
    /// @}
 | 
			
		||||
 | 
			
		||||
    function initBrowser() {
 | 
			
		||||
      window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB
 | 
			
		||||
      window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction
 | 
			
		||||
@@ -469,43 +541,48 @@ function SafeChat() {
 | 
			
		||||
      return window.indexedDB && window.crypto.getRandomValues && Storage
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function register() {
 | 
			
		||||
    var newuser = view.newuser
 | 
			
		||||
 | 
			
		||||
    function chat() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function password() {
 | 
			
		||||
      
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function login() {
 | 
			
		||||
      if (!crypto.key()) register()
 | 
			
		||||
      else password()
 | 
			
		||||
      if (!crypto.key()) newuser(); else password();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function run() {
 | 
			
		||||
    this.run = function() {
 | 
			
		||||
      login()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function start() {
 | 
			
		||||
      view.reboot = run
 | 
			
		||||
    this.start = function() {
 | 
			
		||||
      view.reboot = this.run
 | 
			
		||||
      var compatible = initBrowser()
 | 
			
		||||
      view.checkFeatures()
 | 
			
		||||
      if (!compatible)
 | 
			
		||||
        view.fatal("your browser is not supported")
 | 
			
		||||
      else
 | 
			
		||||
        run()
 | 
			
		||||
        this.run()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //==============================================================================
 | 
			
		||||
 | 
			
		||||
  //------------------------------------------------------------------------------
 | 
			
		||||
  return new Controller(new View())
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
  
 | 
			
		||||
var filecontent = new Array() ///< temporary storage for attachments
 | 
			
		||||
var reboottimer = null
 | 
			
		||||
 | 
			
		||||
function connectionstatus() {
 | 
			
		||||
    if (socket.connected) connected() else disconnected()
 | 
			
		||||
  if (socket.connected) connected(); else disconnected();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Configure local groups
 | 
			
		||||
@@ -673,7 +750,8 @@ function SafeChat() {
 | 
			
		||||
  if (recorder) {
 | 
			
		||||
    $("#videorecorder").hide()
 | 
			
		||||
    recorder.release()
 | 
			
		||||
      delete recorder recorder = null
 | 
			
		||||
    delete recorder
 | 
			
		||||
    recorder = null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -852,10 +930,10 @@ function SafeChat() {
 | 
			
		||||
  console.log("rcv-> messages("+msgs.length+")")
 | 
			
		||||
  if (!password || !privateKey())
 | 
			
		||||
    return setTimeout(function() {emit("messages")}, 1000) // try again later
 | 
			
		||||
    status("allmessages")
 | 
			
		||||
  show("allmessages")
 | 
			
		||||
  notice("load messages, please wait …")
 | 
			
		||||
  msgs.forEach(function(msg) {message(msg, true)})
 | 
			
		||||
    status("chat")
 | 
			
		||||
  show("chat")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Received a message from server
 | 
			
		||||
@@ -905,6 +983,7 @@ function SafeChat() {
 | 
			
		||||
        // not for me
 | 
			
		||||
        success()
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Send Message To Server
 | 
			
		||||
@@ -970,7 +1049,7 @@ function SafeChat() {
 | 
			
		||||
function getpwd() {
 | 
			
		||||
  if (password) return
 | 
			
		||||
  $("#removeKey").show()
 | 
			
		||||
          status("getpwd")
 | 
			
		||||
  show("getpwd")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function deleteUser() {
 | 
			
		||||
@@ -983,7 +1062,7 @@ function SafeChat() {
 | 
			
		||||
function removeKey() {
 | 
			
		||||
  togglemenu()
 | 
			
		||||
  $("#removeKey").hide()
 | 
			
		||||
          status('forgotpassword')
 | 
			
		||||
  show('forgotpassword')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Main Chat Window
 | 
			
		||||
@@ -992,7 +1071,7 @@ function SafeChat() {
 | 
			
		||||
var firsttime = true
 | 
			
		||||
function chat() {
 | 
			
		||||
  if (!password) return getpwd()
 | 
			
		||||
          status("chat")
 | 
			
		||||
    show("chat")
 | 
			
		||||
  if (firsttime && $('#msgs').is(':empty')) {
 | 
			
		||||
    firsttime = false
 | 
			
		||||
    notice("getting previous messages, please wait …")
 | 
			
		||||
@@ -1022,7 +1101,7 @@ function SafeChat() {
 | 
			
		||||
/** Shows user creation form. On submit, a private key is generated in
 | 
			
		||||
   createkeypair(), then login() creates the user. */
 | 
			
		||||
function newuser() {
 | 
			
		||||
          status("newuser")
 | 
			
		||||
  show("newuser")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check if local storage is available
 | 
			
		||||
@@ -1033,7 +1112,7 @@ function SafeChat() {
 | 
			
		||||
    localStorage.removeItem(test)
 | 
			
		||||
    return true
 | 
			
		||||
  } catch(e) {
 | 
			
		||||
            status("nolocalstorage")
 | 
			
		||||
    show("nolocalstorage")
 | 
			
		||||
    error("local storage not available")
 | 
			
		||||
  }
 | 
			
		||||
  return false
 | 
			
		||||
@@ -1043,7 +1122,7 @@ function SafeChat() {
 | 
			
		||||
/** Decide whether to login or to create a new user */
 | 
			
		||||
function start() {
 | 
			
		||||
  $("#menu").hide()
 | 
			
		||||
          //status("startup")
 | 
			
		||||
  //show("startup")
 | 
			
		||||
  if (checkLocalStorage())
 | 
			
		||||
    try {
 | 
			
		||||
      if (!userid()) {
 | 
			
		||||
@@ -1057,7 +1136,13 @@ function SafeChat() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var safechat = new SafeChat()
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
  safechat.start()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function old() {
 | 
			
		||||
  /// On Load, Call @ref start
 | 
			
		||||
  $(window.onbeforeunload = function() {
 | 
			
		||||
    return "Are you sure you want to navigate away?"
 | 
			
		||||
 
 | 
			
		||||
@@ -79,7 +79,7 @@ module.exports = function(sql) {
 | 
			
		||||
                });
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    socket.on("user", function(name) {
 | 
			
		||||
    socket.on("user", function(name, fn) {
 | 
			
		||||
      console.log("-> signal: user("+name+")");
 | 
			
		||||
      var result = {name: name, exists: false, pubkey: null};
 | 
			
		||||
      sql.query("select pubkey from user where name = ?", [name], function(err, res, flds) {
 | 
			
		||||
@@ -87,7 +87,20 @@ module.exports = function(sql) {
 | 
			
		||||
          result.exists = true;
 | 
			
		||||
          result.pubkey = res[0].pubkey;
 | 
			
		||||
        }
 | 
			
		||||
        emit('user', result);
 | 
			
		||||
        fn(result);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    socket.on("lookup", function(name, fn) {
 | 
			
		||||
      console.log("-> signal: lookup("+name+")")
 | 
			
		||||
      var result = false
 | 
			
		||||
      sql.query("select pubkey from user where name = ?",
 | 
			
		||||
                [name],
 | 
			
		||||
                function(err, res, flds) {
 | 
			
		||||
                  if (!err && res && res.length) {
 | 
			
		||||
                    result = true;
 | 
			
		||||
                  }
 | 
			
		||||
                  fn(result)
 | 
			
		||||
                });
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
 
 | 
			
		||||
@@ -51,21 +51,12 @@
 | 
			
		||||
      <div id="newuser" style="display: none">
 | 
			
		||||
        <h2>Register User</h2>
 | 
			
		||||
        <p>All you need to start is a username and a password:</p>
 | 
			
		||||
        <form id="register" autocomplete="off">
 | 
			
		||||
          <input placeholder="username" autocomplete="off" type="text" id="user"/>
 | 
			
		||||
          <input placeholder="password" autocomplete="off" type="password" id="pwd" oninput="checkpwd(this.value, document.getElementById('pwd2').value)"/>
 | 
			
		||||
          <input placeholder="repeat password" autocomplete="off" type="password" id="pwd2" oninput="checkpwd(document.getElementById('pwd').value, this.value)"/>
 | 
			
		||||
          <button id="createuser" disabled>register</button>
 | 
			
		||||
        <form id="register" autocomplete="off" onsubmit="safechat.createuser(document.getElementById('user').value, document.getElementById('pwd').value)">
 | 
			
		||||
          <input placeholder="username" autocomplete="off" type="text" id="user" oninput="safechat.lookup(this.value)" />
 | 
			
		||||
          <input placeholder="password" autocomplete="off" type="password" id="pwd" oninput="safechat.checkpasswords(this.value, document.getElementById('pwd2').value)"/>
 | 
			
		||||
          <input placeholder="repeat password" autocomplete="off" type="password" id="pwd2" oninput="safechat.checkpasswords(document.getElementById('pwd').value, this.value)"/>
 | 
			
		||||
          <input id="createuser" type="submit" value="register" disabled />
 | 
			
		||||
        </form>
 | 
			
		||||
        <script>
 | 
			
		||||
          $("#user").on("input", function() {
 | 
			
		||||
            queryuser($('#user').val());
 | 
			
		||||
          });
 | 
			
		||||
          $("#createuser").on("click", function(event) {
 | 
			
		||||
            createkeypair($('#user').val(), $('#pwd').val());
 | 
			
		||||
            return false;
 | 
			
		||||
          });
 | 
			
		||||
        </script>
 | 
			
		||||
        <p>Please chose any username, e.g. a pseudonym, your e-mail,
 | 
			
		||||
        your phone number, your real name, and chose a safe
 | 
			
		||||
        password.</p>
 | 
			
		||||
@@ -184,9 +175,9 @@
 | 
			
		||||
 | 
			
		||||
      <!--                                                    Fatal: Abort -->
 | 
			
		||||
 | 
			
		||||
      <div id="fatal">
 | 
			
		||||
      <div id="fatal" style="display: none">
 | 
			
		||||
        <h2 id="fatal-msg">Failure</h2>
 | 
			
		||||
        <p>The SafeChat has been aborted due to a fatal error.</p>
 | 
			
		||||
        <p>SafeChat has been aborted due to a fatal error.</p>
 | 
			
		||||
        <p>There is a problem in your browser. Please try to reload. If the problem persists, please update your web browser or try SafeChat in another browser.</p>
 | 
			
		||||
        <p>The following java script features are required:</p>
 | 
			
		||||
        <ul class="features">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user