<commit_before>(function(window){


  //
  // helper methods
  //
  var cEl = document.createElement.bind(document);
  function gCl(parent, className) {
    return parent.getElementsByClassName(className)[0];
  }
  function gTl(parent, className) {
    return parent.getElementsByTagName(className)[0];
  }

  function removeClass(el, className) {
    return el.classList.remove(className);
  }

  function addClass(el, className) {
    return el.classList.add(className);
  }

  function stop_propagation(event) {
    if(typeof(event.stopPropagation) == 'function') {
      event.stopPropagation();
    } else {
      event.cancelBubble = true;
    }
  }


  RemoteStorage.Widget.View = function() {
    if(typeof(document) === 'undefined') {
      throw "Widget not supported";
    }
    RemoteStorage.eventHandling(this,
                                'connect',
                                'disconnect',
                                'sync',
                                'display',
                                'reset');

    // re-binding the event so they can be called from the window
    for(var event in this.events){
      this.events[event] = this.events[event].bind(this);
    }


    // bubble toggling stuff
    this.toggle_bubble = function(event) {
      if(this.bubble.className.search('rs-hidden') < 0) {
        this.hide_bubble(event);
      } else {
        this.show_bubble(event);
      }
    }.bind(this);

    this.hide_bubble = function(){
      //console.log('hide bubble',this);
      addClass(this.bubble, 'rs-hidden')
      document.body.removeEventListener('click', hide_bubble_on_body_click);
    }.bind(this);

    var hide_bubble_on_body_click = function (event) {
      for(var p = event.target; p != document.body; p = p.parentElement) {
        if(p.id == 'remotestorage-widget') {
          return;
        }
      }
      this.hide_bubble();
    }.bind(this);

    this.show_bubble = function(event){
      //console.log('show bubble',this.bubble,event)
      removeClass(this.bubble, 'rs-hidden');
      if(typeof(event) != 'undefined') {
         stop_propagation(event);
       }
      document.body.addEventListener('click', hide_bubble_on_body_click);
      gTl(this.bubble,'form').userAddress.focus();
    }.bind(this);


    this.display = function(domID) {

      if(typeof(this.div) !== 'undefined')
        return this.div;

      var element = cEl('div');
      var style = cEl('style');
      style.innerHTML = RemoteStorage.Assets.widgetCss;

      element.id = "remotestorage-widget";

      element.innerHTML = RemoteStorage.Assets.widget;


      element.appendChild(style);
      if(domID) {
        var parent = document.getElementById(domID);
        if(! parent) {
          throw "Failed to find target DOM element with id=\"" + domID + "\"";
        }
        parent.appendChild(element);
      } else {
        document.body.appendChild(element);
      }

      var el;
      //sync button
      el = gCl(element, 'rs-sync');
      gTl(el, 'img').src = RemoteStorage.Assets.syncIcon;
      el.addEventListener('click', this.events.sync);

      //disconnect button
      el = gCl(element, 'rs-disconnect');
      gTl(el, 'img').src = RemoteStorage.Assets.disconnectIcon;
      el.addEventListener('click', this.events.disconnect);


      //get me out of here
      var el = gCl(element, 'remotestorage-reset').addEventListener('click', this.events.reset);
      //connect button
      var cb = gCl(element,'connect');
      gTl(cb, 'img').src = RemoteStorage.Assets.connectIcon;
      cb.addEventListener('click', this.events.connect);


      // input
      this.form = gTl(element, 'form')
      el = this.form.userAddress;
      el.addEventListener('keyup', function(event) {
        if(event.target.value) cb.removeAttribute('disabled');
        else cb.setAttribute('disabled','disabled');
      });
      if(this.userAddress) {
        el.value = this.userAddress;
      }

      //the cube
      el = gCl(element, 'rs-cube');
      el.src = RemoteStorage.Assets.remoteStorageIcon;
      el.addEventListener('click', this.toggle_bubble);
      this.cube = el

      //the bubble
      this.bubble = gCl(element,'rs-bubble');
      // what is the meaning of this hiding the b
      var bubbleDontCatch = { INPUT: true, BUTTON: true, IMG: true };
      this.bubble.addEventListener('click', function(event) {
        if(! bubbleDontCatch[event.target.tagName] && ! (this.div.classList.contains('remotestorage-state-unauthorized') )) {

          this.show_bubble(event);
        };
      }.bind(this))
      this.hide_bubble();

      this.div = element;

      this.states.initial.call(this);
      this.events.display.call(this);
      return this.div;
    };

  }

  RemoteStorage.Widget.View.prototype = {

    // Methods:
    //
    //  display(domID)
    //    draws the widget inside of the dom element with the id domID
    //   returns: the widget div
    //
    //  showBubble()
    //    shows the bubble
    //  hideBubble()
    //    hides the bubble
    //  toggleBubble()
    //    shows the bubble when hidden and the other way around
    //
    //  setState(state, args)
    //    calls states[state]
    //    args are the arguments for the
    //    state(errors mostly)
    //
    // setUserAddres
    //    set userAddress of the input field
    //
    // States:
    //  initial      - not connected
    //  authing      - in auth flow
    //  connected    - connected to remote storage, not syncing at the moment
    //  busy         - connected, syncing at the moment
    //  offline      - connected, but no network connectivity
    //  error        - connected, but sync error happened
    //  unauthorized - connected, but request returned 401
    //
    // Events:
    // connect    : fired when the connect button is clicked
    // sync       : fired when the sync button is clicked
    // disconnect : fired when the disconnect button is clicked
    // reset      : fired after crash triggers disconnect
    // display    : fired when finished displaying the widget
    setState : function(state, args) {
      RemoteStorage.log('widget.view.setState(',state,',',args,');');
      var s = this.states[state];
      if(typeof(s) === 'undefined') {
        throw new Error("Bad State assigned to view: " + state);
      }
      s.apply(this,args);
    },
    setUserAddress : function(addr) {
      this.userAddress = addr || '';

      var el;
      if(this.div && (el = gTl(this.div, 'form').userAddress)) {
        el.value = this.userAddress;
      }
    },

    states :  {
      initial : function(message) {
        var cube = this.cube;
        var info = message || 'This app allows you to use your own storage! Find more info on <a href="http://remotestorage.io/" target="_blank">remotestorage.io';
        if(message) {
          cube.src = RemoteStorage.Assets.remoteStorageIconError;
          removeClass(this.cube, 'remotestorage-loading');
          this.show_bubble();
          setTimeout(function(){
            cube.src = RemoteStorage.Assets.remoteStorageIcon;
          //presumably this timeout would give the browser time to load the assets? timeout 0 seems to work in FF, Chrome and Opera though, so removing it:
          // -- Michiel
          //},3512)
          },0)
        } else {
          this.hide_bubble();
        }
        this.div.className = "remotestorage-state-initial";
        gCl(this.div, 'rs-status-text').innerHTML = "Connect <strong>remotestorage</strong>";

        //if address not empty connect button enabled
        //TODO check if this works
        var cb = gCl(this.div, 'connect')<commit_msg>@ggrin explained that this is what that timeout was for. made it a bit less arbitrary, and documented it<commit_after>(function(window){


  //
  // helper methods
  //
  var cEl = document.createElement.bind(document);
  function gCl(parent, className) {
    return parent.getElementsByClassName(className)[0];
  }
  function gTl(parent, className) {
    return parent.getElementsByTagName(className)[0];
  }

  function removeClass(el, className) {
    return el.classList.remove(className);
  }

  function addClass(el, className) {
    return el.classList.add(className);
  }

  function stop_propagation(event) {
    if(typeof(event.stopPropagation) == 'function') {
      event.stopPropagation();
    } else {
      event.cancelBubble = true;
    }
  }


  RemoteStorage.Widget.View = function() {
    if(typeof(document) === 'undefined') {
      throw "Widget not supported";
    }
    RemoteStorage.eventHandling(this,
                                'connect',
                                'disconnect',
                                'sync',
                                'display',
                                'reset');

    // re-binding the event so they can be called from the window
    for(var event in this.events){
      this.events[event] = this.events[event].bind(this);
    }


    // bubble toggling stuff
    this.toggle_bubble = function(event) {
      if(this.bubble.className.search('rs-hidden') < 0) {
        this.hide_bubble(event);
      } else {
        this.show_bubble(event);
      }
    }.bind(this);

    this.hide_bubble = function(){
      //console.log('hide bubble',this);
      addClass(this.bubble, 'rs-hidden')
      document.body.removeEventListener('click', hide_bubble_on_body_click);
    }.bind(this);

    var hide_bubble_on_body_click = function (event) {
      for(var p = event.target; p != document.body; p = p.parentElement) {
        if(p.id == 'remotestorage-widget') {
          return;
        }
      }
      this.hide_bubble();
    }.bind(this);

    this.show_bubble = function(event){
      //console.log('show bubble',this.bubble,event)
      removeClass(this.bubble, 'rs-hidden');
      if(typeof(event) != 'undefined') {
         stop_propagation(event);
       }
      document.body.addEventListener('click', hide_bubble_on_body_click);
      gTl(this.bubble,'form').userAddress.focus();
    }.bind(this);


    this.display = function(domID) {

      if(typeof(this.div) !== 'undefined')
        return this.div;

      var element = cEl('div');
      var style = cEl('style');
      style.innerHTML = RemoteStorage.Assets.widgetCss;

      element.id = "remotestorage-widget";

      element.innerHTML = RemoteStorage.Assets.widget;


      element.appendChild(style);
      if(domID) {
        var parent = document.getElementById(domID);
        if(! parent) {
          throw "Failed to find target DOM element with id=\"" + domID + "\"";
        }
        parent.appendChild(element);
      } else {
        document.body.appendChild(element);
      }

      var el;
      //sync button
      el = gCl(element, 'rs-sync');
      gTl(el, 'img').src = RemoteStorage.Assets.syncIcon;
      el.addEventListener('click', this.events.sync);

      //disconnect button
      el = gCl(element, 'rs-disconnect');
      gTl(el, 'img').src = RemoteStorage.Assets.disconnectIcon;
      el.addEventListener('click', this.events.disconnect);


      //get me out of here
      var el = gCl(element, 'remotestorage-reset').addEventListener('click', this.events.reset);
      //connect button
      var cb = gCl(element,'connect');
      gTl(cb, 'img').src = RemoteStorage.Assets.connectIcon;
      cb.addEventListener('click', this.events.connect);


      // input
      this.form = gTl(element, 'form')
      el = this.form.userAddress;
      el.addEventListener('keyup', function(event) {
        if(event.target.value) cb.removeAttribute('disabled');
        else cb.setAttribute('disabled','disabled');
      });
      if(this.userAddress) {
        el.value = this.userAddress;
      }

      //the cube
      el = gCl(element, 'rs-cube');
      el.src = RemoteStorage.Assets.remoteStorageIcon;
      el.addEventListener('click', this.toggle_bubble);
      this.cube = el

      //the bubble
      this.bubble = gCl(element,'rs-bubble');
      // what is the meaning of this hiding the b
      var bubbleDontCatch = { INPUT: true, BUTTON: true, IMG: true };
      this.bubble.addEventListener('click', function(event) {
        if(! bubbleDontCatch[event.target.tagName] && ! (this.div.classList.contains('remotestorage-state-unauthorized') )) {

          this.show_bubble(event);
        };
      }.bind(this))
      this.hide_bubble();

      this.div = element;

      this.states.initial.call(this);
      this.events.display.call(this);
      return this.div;
    };

  }

  RemoteStorage.Widget.View.prototype = {

    // Methods:
    //
    //  display(domID)
    //    draws the widget inside of the dom element with the id domID
    //   returns: the widget div
    //
    //  showBubble()
    //    shows the bubble
    //  hideBubble()
    //    hides the bubble
    //  toggleBubble()
    //    shows the bubble when hidden and the other way around
    //
    //  setState(state, args)
    //    calls states[state]
    //    args are the arguments for the
    //    state(errors mostly)
    //
    // setUserAddres
    //    set userAddress of the input field
    //
    // States:
    //  initial      - not connected
    //  authing      - in auth flow
    //  connected    - connected to remote storage, not syncing at the moment
    //  busy         - connected, syncing at the moment
    //  offline      - connected, but no network connectivity
    //  error        - connected, but sync error happened
    //  unauthorized - connected, but request returned 401
    //
    // Events:
    // connect    : fired when the connect button is clicked
    // sync       : fired when the sync button is clicked
    // disconnect : fired when the disconnect button is clicked
    // reset      : fired after crash triggers disconnect
    // display    : fired when finished displaying the widget
    setState : function(state, args) {
      RemoteStorage.log('widget.view.setState(',state,',',args,');');
      var s = this.states[state];
      if(typeof(s) === 'undefined') {
        throw new Error("Bad State assigned to view: " + state);
      }
      s.apply(this,args);
    },
    setUserAddress : function(addr) {
      this.userAddress = addr || '';

      var el;
      if(this.div && (el = gTl(this.div, 'form').userAddress)) {
        el.value = this.userAddress;
      }
    },

    states :  {
      initial : function(message) {
        var cube = this.cube;
        var info = message || 'This app allows you to use your own storage! Find more info on <a href="http://remotestorage.io/" target="_blank">remotestorage.io';
        if(message) {
          cube.src = RemoteStorage.Assets.remoteStorageIconError;
          removeClass(this.cube, 'remotestorage-loading');
          this.show_bubble();
          setTimeout(function(){
            cube.src = RemoteStorage.Assets.remoteStorageIcon;
          },5000)//show the red error cube for 5 seconds, then show the normal orange one again
        } else {
          this.hide_bubble();
        }
        this.div.className = "remotestorage-state-initial";
        gCl(this.div, 'rs-status-text').innerHTML = "Connect <strong>remotestorage</strong>";

        //if address not empty connect button enabled
        //TODO check if this works
        var cb = gCl(this.div, 'connect')
{'fraction_non_alphanumeric': 0.12877697841726618, 'fraction_numerical': 0.0015042511445389143, 'mean_word_length': 2.216449305847707, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1139750', 'n_tokens_mistral': 4555, 'n_tokens_neox': 4451, 'n_words': 1021}