<commit_before>'use strict';

((self, isBrowser) => {
  if(!isBrowser) {
    self.ito = require('./ito.js');
    self.ItoProvider = self.ito.ItoProvider;
  }

  if(!self.ito) {
    throw new Error('Ito base library has not been loaded yet.');
  }

  if(!ito.provider)
    ito.provider = {};
  function FirebaseProvider() {};
  Object.setPrototypeOf(FirebaseProvider.prototype, ItoProvider.prototype);
  ito.provider.firebase = new FirebaseProvider();
  let firebaseProvider = ito.provider.firebase;

  /*
   * Firebase Login
   */
  let provider = null;
  let credential = null;
  let initResolve = null;
  let email = null;
  let userName = null;

  firebaseProvider.load = () => {
    // Initialize Firebase client
    if(!self.firebase) {
      // Browser
      if(isBrowser) {
        let h = document.querySelector('head');
        return new Promise((resolve, reject) => {
          let s = document.createElement('script');
          s.src = 'https://www.gstatic.com/firebasejs/3.5.2/firebase.js';
          s.addEventListener('load', () => { resolve(); });
          s.addEventListener('error', () => { reject(); });
          h.appendChild(s);
        });
      }
      // Node.js
      else {
        self.firebase = require('firebase');
        return Promise.resolve();
      }
    }
    else
      return Promise.resolve();
  };

  firebaseProvider.init = arg => {
    return new Promise((resolve, reject) => {
      initResolve = resolve;
      firebase.initializeApp({
        apiKey: arg.apiKey,
        authDomain: arg.authDomain,
        databaseURL: arg.databaseURL
      });
      firebase.auth().onAuthStateChanged(user => {
        let b = !!user;
        if(initResolve) {
          let r = initResolve;
          initResolve = null;
          if(user)
            firebaseGetProfile()
              .then(firebaseSetOnDisconnectRef)
              .then(r.bind(this, b));
          else
            r(b);
        }
        else {
          if(user)
            firebaseSetOnDisconnectRef();
          firebaseProvider.onOnline(b);
        }

        // Disconnect/Reconnect to Firebase
        firebase.database().ref('.info/connected').on('value', snapshot => {
          if(snapshot.val() === true) {
            if(disconnectRef && getUser()) {
              disconnectRef.set('online');
              firebaseSetOnDisconnectRef();
              if(passcode) {
                let p = passcode;
                passcode = null;
                firebaseSetPasscodeRef(p);
              }
            }
            firebaseProvider.onOnline(!!getUser());
          }
          else {
            firebaseResetPasscodeRef(true);
            firebaseProvider.onDisconnect();
          }
        });
      });
    });
  }

  function getUser() {
    return firebase.auth().currentUser;
  }
  firebaseProvider.getUser = () => {
    let user = getUser();
    return user ? {
      userName: userName,
      email: email,
      isAnonymous: user.isAnonymous,
      uid: user.uid
    } : null;
  };

  firebaseProvider.signIn = {
    anonymous: () => {
      return firebase.auth().signInAnonymously().then(() => {
        return firebaseSetProfile();
      });
    },
    google: () => {
      provider = new firebase.auth.GoogleAuthProvider();
      provider.addScope('email');
      return new Promise((resolve, reject) => {
        firebase.auth().signInWithPopup(provider).then(result => {
          credential = result.credential;
          fetch('https://www.googleapis.com/userinfo/v2/me',
            { headers: { Authorization: 'Bearer ' + credential.accessToken }}
          ).then(response => {
            return response.json();
          }).then(json => {
            email = json.email;
            return firebaseSetProfile();
          }).then(p => {
            resolve(p);
          });
        }, error => {
          reject(error);
        })
      });
    },
    facebook: () => {
      provider = new firebase.auth.FacebookAuthProvider();
      provider.addScope('email');
      return new Promise((resolve, reject) => {
        firebase.auth().signInWithPopup(provider).then(result => {
          credential = result.credential;
          fetch('https://graph.facebook.com/v2.7/me?fields=email&access_token=' + credential.accessToken
          ).then(response => {
            return response.json();
          }).then(json => {
            email = json.email;
            return firebaseSetProfile();
          }).then(p => {
            resolve(p);
          });
        }, error => {
          reject(error);
        })
      });
    },
    email: (id, pass) => {
      return new Promise((resolve, reject) => {
        firebase.auth().signInWithEmailAndPassword(id, pass).then(user => {
          email = user.email;
          resolve(firebaseSetProfile());
        });
      }, error => {
        reject(error);
      });
    }
  };

  firebaseProvider.createUser = (id, pass) => {
    return new Promise((resolve, reject) => {
      firebase.auth().createUserWithEmailAndPassword(id, pass).then(user => {
        return firebaseSetProfile(true);
      }).then(p => {
        firebaseProvider.signOut().then(() => {
          resolve(p);
        });
      });
    }, error => {
      reject(error);
    });
  };

  firebaseProvider.signOut = () => {
    return new Promise((resolve, reject) => {
      firebaseSetOffline().then(() => {
        let user = getUser();
        if(user && user.isAnonymous) {
          return firebase.database().ref('lastNotificationChecked/' + user.uid).remove()
            .then(firebase.database().ref('emails/' + firebaseEscape(email)).remove())
            .then(firebaseDeleteProfile())
            .then(() => { user.delete(); });
        }
        else
          return firebase.auth().signOut();
      }).then(() => {
        resolve();
      }, error => {
        reject(error);
      })
    });
  };

  /*
   * Firebase Database: User Accounts and Status
   */
  let disconnectRef = null;
  let requestRef = null;
  let friendsRef = null;
  let profilesRef = {};
  let isAdmin = false;
  let passcodesRef = null;
  let passcode = null;

  function firebaseEscape(s) {
    return s ? s.replace(/[\W_]/g, c=>{return '%' + c.charCodeAt(0).toString(16);}) : null;
  }
  firebaseProvider.escape = firebaseEscape;

  function firebaseCheckAdministrator() {
    let user = getUser();
    return firebase.database().ref('administrators/' + firebaseEscape(email))
      .once('value').then(v => {
        isAdmin = !!v && v.val() === true;
      }, () => {
        isAdmin = false;
      });
  }

  function firebaseSetProfile(createOnly) {
    let user = getUser();
    email = email || user.uid;
    userName = user.displayName || email;
    let prof = {
      userName: userName,
      email: email,
      emailEscaped: firebaseEscape(email),
      status: createOnly ? 'offline' : 'online'
    };
    let p = firebase.database().ref('users/' + user.uid).set(prof)
      .then(firebase.database().ref('emails/' + firebaseEscape(email)).set(user.uid))
      .then(firebaseCheckAdministrator);
    if(!createOnly)
      firebaseOnOnline();
    return p.then(() => { return prof; });
  }

  function firebaseGetProfile() {
    return new Promise((resolve, reject) => {
      let user = getUser();
      firebase.database().ref('users/' + user.uid).once('value', snapshot => {
        email = snapshot.val().email;
        firebase.database().ref('users/' + user.uid + '/status').set('online')
          .then(firebase.database().ref('emails/' + firebaseEscape(email)).set(user.uid))
          .then(firebaseCheckAdministrator)
          .then(firebaseOnOnline)
          .then(resolve);
      });
    });
  }

  function firebaseGetFriendProfile(uid) {
    return new Promise((resolve, reject) => {
      firebase.database().ref('users/' + uid).once('value', snapshot => {
        resolve(snapshot.val());
      });
    });
  }

  function firebaseOnRequest(usePasscode, data) {
    if(data) {
      let v = data.val();
      let r = data.ref;
      switch(v.type) {
      case 'request':
        firebaseProvider.onRequest(data.key, {
          userName: v.userName,
          uid: v.uid,
          email: v.email
        }, usePasscode, v.options || null);
        break;
      case 'accept':
        dropRequest(data.key, usePasscode).then(() => {
          return firebaseAddFriend(v.uid);
        }).then(() => {
          firebaseProvider.onAccept(v.requestKey, {
            userName: v.userName,
            uid: v.uid,
            email: v.email
          });
        });
        break;
      case 'reject':
        dropRequest(data.key, usePasscode).then(() => {
          firebaseProvider.onReject(v.requestKey);
        });
        break;
      case 'remove':
        firebaseRemoveFriend(v.uid);
        dropRequest(data.key, usePasscode);
        break;
      }
    }
  }

  function firebaseSetRequestRef() {
    let escaped = firebaseEscape(email);
    requestRef = firebase.database().ref('requests/' + escaped);
    requestRef.once('value').then(data => {
      let val = data.val();
      let r = data.ref;
      return val ? Object.keys(val).reduce((p, k) => {
        let v = val[k];
        if(v.uid) {
          return p.then(
            firebase.database().ref('users/' + v.uid).once('value').then(d => {
              if(!d || !d.val()) {
                r.child(k).remove();
                firebase.database().ref('requestKeys/' + v.uid + '/' + escaped).remove();
              }
            })
          );
        }
        else
          return p;
      }, Promise.resolve()) : Promise.resolve();
    }).then(() => {
      requestRef.on('child_added', firebaseOnRequest.bind(this, false));
    });
  }

  function firebaseSetPasscodeRef(pass) {
    return new Promise((resolve, reject) => {
      if(passcode === pass) {
        resolve();
      }
      else if(!pass) {
        firebaseResetPasscodeRef();
        resolve();
      }
      else {
        let user = getUser();
        if(pass)
          firebaseResetPasscodeRef();
        let passRef = firebase.database().ref('passcodes/' + user.uid);
        passRef.onDisconnect().remove();
        return passRef.set(pass)<commit_msg>Fix an uncaught promise during removal of a user<commit_after>'use strict';

((self, isBrowser) => {
  if(!isBrowser) {
    self.ito = require('./ito.js');
    self.ItoProvider = self.ito.ItoProvider;
  }

  if(!self.ito) {
    throw new Error('Ito base library has not been loaded yet.');
  }

  if(!ito.provider)
    ito.provider = {};
  function FirebaseProvider() {};
  Object.setPrototypeOf(FirebaseProvider.prototype, ItoProvider.prototype);
  ito.provider.firebase = new FirebaseProvider();
  let firebaseProvider = ito.provider.firebase;

  /*
   * Firebase Login
   */
  let provider = null;
  let credential = null;
  let initResolve = null;
  let email = null;
  let userName = null;

  firebaseProvider.load = () => {
    // Initialize Firebase client
    if(!self.firebase) {
      // Browser
      if(isBrowser) {
        let h = document.querySelector('head');
        return new Promise((resolve, reject) => {
          let s = document.createElement('script');
          s.src = 'https://www.gstatic.com/firebasejs/3.5.2/firebase.js';
          s.addEventListener('load', () => { resolve(); });
          s.addEventListener('error', () => { reject(); });
          h.appendChild(s);
        });
      }
      // Node.js
      else {
        self.firebase = require('firebase');
        return Promise.resolve();
      }
    }
    else
      return Promise.resolve();
  };

  firebaseProvider.init = arg => {
    return new Promise((resolve, reject) => {
      initResolve = resolve;
      firebase.initializeApp({
        apiKey: arg.apiKey,
        authDomain: arg.authDomain,
        databaseURL: arg.databaseURL
      });
      firebase.auth().onAuthStateChanged(user => {
        let b = !!user;
        if(initResolve) {
          let r = initResolve;
          initResolve = null;
          if(user)
            firebaseGetProfile()
              .then(firebaseSetOnDisconnectRef)
              .then(r.bind(this, b));
          else
            r(b);
        }
        else {
          if(user)
            firebaseSetOnDisconnectRef();
          firebaseProvider.onOnline(b);
        }

        // Disconnect/Reconnect to Firebase
        firebase.database().ref('.info/connected').on('value', snapshot => {
          if(snapshot.val() === true) {
            if(disconnectRef && getUser()) {
              disconnectRef.set('online');
              firebaseSetOnDisconnectRef();
              if(passcode) {
                let p = passcode;
                passcode = null;
                firebaseSetPasscodeRef(p);
              }
            }
            firebaseProvider.onOnline(!!getUser());
          }
          else {
            firebaseResetPasscodeRef(true);
            firebaseProvider.onDisconnect();
          }
        });
      });
    });
  }

  function getUser() {
    return firebase.auth().currentUser;
  }
  firebaseProvider.getUser = () => {
    let user = getUser();
    return user ? {
      userName: userName,
      email: email,
      isAnonymous: user.isAnonymous,
      uid: user.uid
    } : null;
  };

  firebaseProvider.signIn = {
    anonymous: () => {
      return firebase.auth().signInAnonymously().then(() => {
        return firebaseSetProfile();
      });
    },
    google: () => {
      provider = new firebase.auth.GoogleAuthProvider();
      provider.addScope('email');
      return new Promise((resolve, reject) => {
        firebase.auth().signInWithPopup(provider).then(result => {
          credential = result.credential;
          fetch('https://www.googleapis.com/userinfo/v2/me',
            { headers: { Authorization: 'Bearer ' + credential.accessToken }}
          ).then(response => {
            return response.json();
          }).then(json => {
            email = json.email;
            return firebaseSetProfile();
          }).then(p => {
            resolve(p);
          });
        }, error => {
          reject(error);
        })
      });
    },
    facebook: () => {
      provider = new firebase.auth.FacebookAuthProvider();
      provider.addScope('email');
      return new Promise((resolve, reject) => {
        firebase.auth().signInWithPopup(provider).then(result => {
          credential = result.credential;
          fetch('https://graph.facebook.com/v2.7/me?fields=email&access_token=' + credential.accessToken
          ).then(response => {
            return response.json();
          }).then(json => {
            email = json.email;
            return firebaseSetProfile();
          }).then(p => {
            resolve(p);
          });
        }, error => {
          reject(error);
        })
      });
    },
    email: (id, pass) => {
      return new Promise((resolve, reject) => {
        firebase.auth().signInWithEmailAndPassword(id, pass).then(user => {
          email = user.email;
          resolve(firebaseSetProfile());
        });
      }, error => {
        reject(error);
      });
    }
  };

  firebaseProvider.createUser = (id, pass) => {
    return new Promise((resolve, reject) => {
      firebase.auth().createUserWithEmailAndPassword(id, pass).then(user => {
        return firebaseSetProfile(true);
      }).then(p => {
        firebaseProvider.signOut().then(() => {
          resolve(p);
        });
      });
    }, error => {
      reject(error);
    });
  };

  firebaseProvider.signOut = () => {
    return new Promise((resolve, reject) => {
      firebaseSetOffline().then(() => {
        let user = getUser();
        if(user && user.isAnonymous) {
          return firebase.database().ref('lastNotificationChecked/' + user.uid).remove()
            .then(firebase.database().ref('emails/' + firebaseEscape(email)).remove())
            .then(firebaseDeleteProfile())
            .then(() => { user.delete(); });
        }
        else
          return firebase.auth().signOut();
      }).then(() => {
        resolve();
      }, error => {
        reject(error);
      })
    });
  };

  /*
   * Firebase Database: User Accounts and Status
   */
  let disconnectRef = null;
  let requestRef = null;
  let friendsRef = null;
  let profilesRef = {};
  let isAdmin = false;
  let passcodesRef = null;
  let passcode = null;

  function firebaseEscape(s) {
    return s ? s.replace(/[\W_]/g, c=>{return '%' + c.charCodeAt(0).toString(16);}) : null;
  }
  firebaseProvider.escape = firebaseEscape;

  function firebaseCheckAdministrator() {
    let user = getUser();
    return firebase.database().ref('administrators/' + firebaseEscape(email))
      .once('value').then(v => {
        isAdmin = !!v && v.val() === true;
      }, () => {
        isAdmin = false;
      });
  }

  function firebaseSetProfile(createOnly) {
    let user = getUser();
    email = email || user.uid;
    userName = user.displayName || email;
    let prof = {
      userName: userName,
      email: email,
      emailEscaped: firebaseEscape(email),
      status: createOnly ? 'offline' : 'online'
    };
    let p = firebase.database().ref('users/' + user.uid).set(prof)
      .then(firebase.database().ref('emails/' + firebaseEscape(email)).set(user.uid))
      .then(firebaseCheckAdministrator);
    if(!createOnly)
      firebaseOnOnline();
    return p.then(() => { return prof; });
  }

  function firebaseGetProfile() {
    return new Promise((resolve, reject) => {
      let user = getUser();
      firebase.database().ref('users/' + user.uid).once('value', snapshot => {
        email = snapshot.val().email;
        firebase.database().ref('users/' + user.uid + '/status').set('online')
          .then(firebase.database().ref('emails/' + firebaseEscape(email)).set(user.uid))
          .then(firebaseCheckAdministrator)
          .then(firebaseOnOnline)
          .then(resolve);
      });
    });
  }

  function firebaseGetFriendProfile(uid) {
    return new Promise((resolve, reject) => {
      firebase.database().ref('users/' + uid).once('value', snapshot => {
        resolve(snapshot.val());
      });
    });
  }

  function firebaseOnRequest(usePasscode, data) {
    if(data) {
      let v = data.val();
      let r = data.ref;
      switch(v.type) {
      case 'request':
        firebaseProvider.onRequest(data.key, {
          userName: v.userName,
          uid: v.uid,
          email: v.email
        }, usePasscode, v.options || null);
        break;
      case 'accept':
        dropRequest(data.key, usePasscode).then(() => {
          return firebaseAddFriend(v.uid);
        }).then(() => {
          firebaseProvider.onAccept(v.requestKey, {
            userName: v.userName,
            uid: v.uid,
            email: v.email
          });
        });
        break;
      case 'reject':
        dropRequest(data.key, usePasscode).then(() => {
          firebaseProvider.onReject(v.requestKey);
        });
        break;
      case 'remove':
        firebaseRemoveFriend(v.uid);
        dropRequest(data.key, usePasscode);
        break;
      }
    }
  }

  function firebaseSetRequestRef() {
    let escaped = firebaseEscape(email);
    requestRef = firebase.database().ref('requests/' + escaped);
    requestRef.once('value').then(data => {
      let val = data.val();
      let r = data.ref;
      return val ? Object.keys(val).reduce((p, k) => {
        let v = val[k];
        if(v.uid) {
          return p.then(
            firebase.database().ref('users/' + v.uid).once('value').then(d => {
              if(!d || !d.val()) {
                r.child(k).remove();
                firebase.database().ref('requestKeys/' + v.uid + '/' + escaped).remove();
              }
            }, () => { /* Removal of the user might be in progress... */ })
          );
        }
        else
          return p;
      }, Promise.resolve()) : Promise.resolve();
    }).then(() => {
      requestRef.on('child_added', firebaseOnRequest.bind(this, false));
    });
  }

  function firebaseSetPasscodeRef(pass) {
    return new Promise((resolve, reject) => {
      if(passcode === pass) {
        resolve();
      }
      else if(!pass) {
        firebaseResetPasscodeRef();
        resolve();
      }
      else {
        let user = getUser();
        if(pass)
          firebaseResetPasscodeRef();
        let passRef = firebase.database().ref('passcodes/' + user.uid);
        passRef.onDisconnect().remove();
        return passRef.set(pass)
{'fraction_non_alphanumeric': 0.15564012490242, 'fraction_numerical': 0.0008782201405152225, 'mean_word_length': 2.005425219941349, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 120, 'https://': 6, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1833365', 'n_tokens_mistral': 6026, 'n_tokens_neox': 5962, 'n_words': 1043}