<commit_before>(function($) {

  function TypeSuggest(el, data, options){
    var defaults = {
      min_characters: 2,
      display_key: undefined,
      search_key: undefined,
      ignored: [
        ' ',
        ',',
        '.'
      ],
      ajax: false,
      url: '',
      param: 'q',
      beforeSend: undefined,
      callback: undefined
    };

    this.config = $.extend({}, defaults, options);

    // set parent element
    this.$el = $(el).addClass("typesuggest");

    this.data = data;

    this.init();
    this.events();

  }

  TypeSuggest.prototype.init = function(){
    var self = this;

    self.$fields = self.$el.find("input").addClass("typesuggest-input").attr('autocomplete', 'off');
    self.$suggest = $('
').appendTo(self.$el);
  }

  TypeSuggest.prototype.events = function(){
    var self = this;

    self.$fields.on('keydown', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      // if up or down arrows, scroll thru list. if enter key, select item
      if(key == 40 || key == 38 || key == 13){

        if(e.keyCode == 40){ // down arrow

          self.$suggest.addClass("selecting");
          self.move(+1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 38){ // up arrow

          self.$suggest.addClass("selecting");
          self.move(-1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 13){ // enter

          var $selectedItem = self.$suggest.find(".selected");
          var data = $selectedItem.data();

          self.selectItem(data);

          // prevent caret from moving
          return false;

        }else{ // everything else

          self.$suggest.removeClass("selecting");

        }

      }

    })

    self.$fields.on('keyup', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      self.$currentField = $field;

      // if esc key, hide suggestions
      if(key === 27){
        self.updateList(true);
      }

      // if not up or down arrows or enter key, fill suggestion
      if(key !== 40 && key !== 38 && key !== 13 && key !== 27){

        if(!self.debounce){

          // get value
          var val = $field.val();

          self.options = [];

          if(val.length && val.length >= self.config.min_characters){

            self.startDebounce();

            if(self.config.ajax){
              if(self.config.beforeSend){
                val = self.config.beforeSend(val);
              }

              if(self.config.url.indexOf('?') > -1){
                var url = self.config.url + "&" + self.config.param + "=" + val;
              }else{
                var url = self.config.url + "?" + self.config.param + "=" + val;
              }

              $.ajax({
                url: url,
                type: 'GET',
                dataType: 'json',
                success: function(data, textStatus, xhr){
                  self.options = data[self.config.results_key];

                  self.updateList();
                }
              })
            }else{
              // compare value to provided list
              $.each(self.data, function(i, item){
                var valLower = val.toLowerCase();
                var itemLower;

                if(self.config.search_key){
                  itemLower = item[self.config.search_key].toLowerCase();
                }else{
                  itemLower = item.toLowerCase();
                }

                if(itemLower.search(valLower) > -1){
                  self.options.push(item);
                }
              })

            }

          }

          self.updateList();
        }
      }else{
        // prevent caret from moving
        return false;
      }
    })

    self.$suggest.on('click', 'li', function(){
      var data = $(this).data();

      self.selectItem(data);
      self.config.callback && self.config.callback(data);
    })

    self.$suggest.on('mouseover', 'li', function(){
      var $li = $(this);

      self.$suggest.find("li").removeClass("selected");
      $li.addClass("selected");
    })

    // if click outside the box, hide suggestions
    $('html').on('click', function(){
      self.updateList(true);
    })
  }

  TypeSuggest.prototype.move = function(where){
    var self = this;
    var $selectedItem = self.$suggest.find(".selected");
    var firstSelected = self.$suggest.find("li:first").hasClass("selected");
    var lastSelected = self.$suggest.find("li:last").hasClass("selected");

    if($selectedItem.length && where > 0){

      if(lastSelected){
        // select first
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:first").addClass("selected");
      }else{
        // selection exists, go to next
        $selectedItem.removeClass("selected").next("li").addClass("selected")
      }

    }
    else if($selectedItem.length && where < 0){

      if(firstSelected){
        // select last
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:last").addClass("selected");
      }else{
        // selection exists, go to prev
        $selectedItem.removeClass("selected").prev("li").addClass("selected")
      }

    }
    else{

      // select first
      self.$suggest.find("li:first").addClass("selected");

    }
  }

  TypeSuggest.prototype.updateList = function(clear){
    var self = this;
    var $list = $('
');

    if(self.options && self.options.length && !clear){
      self.$suggest.addClass("selecting");

      var options = self.options;
      $.each(options, function(i, option){
        var $listItem = $("
").data("option", option);

        if(self.config.display_keys){
          var output = '';

          $.each(self.config.display_keys, function(i, key){
            output += '' + option[key] + ''
          })

          $listItem.html(output);
        }else{
          $listItem.html(option);
        }

        $list.append($listItem);
      })

      self.$suggest.html($list);

    }else{
      self.$suggest.removeClass("selecting").empty();;
    }
  }

  TypeSuggest.prototype.selectItem = function(data){
    var self = this;

    self.options = [];
    self.updateList(true);

    if(!self.config.display_keys){
      self.$currentField.val(data.option);
    }

    self.config.callback && self.config.callback(data);
  }

  TypeSuggest.prototype.startDebounce = function(){
    var self = this;

    self.debounce = true;

    setTimeout(function(){
      self.debounce = false;
    }, 250)
  }

  $.fn.suggest = function(data, options){
    var suggest = new TypeSuggest(this, data, options);
    return this;
  }

})(jQuery);<commit_msg>add ignored keys, add multiple search keys and arrays as search keys<commit_after>(function($) {

  function TypeSuggest(el, data, options){
    var defaults = {
      min_characters: 2,
      display_key: undefined,
      search_keys: undefined,
      ignored: [
        ' ',
        ',',
        '.'
      ],
      ajax: false,
      url: '',
      param: 'q',
      beforeSend: undefined,
      callback: undefined,
      ignored_key: undefined
    };

    this.config = $.extend({}, defaults, options);

    // set parent element
    this.$el = $(el).addClass("typesuggest");

    this.data = data;

    this.init();
    this.events();

  }

  TypeSuggest.prototype.init = function(){
    var self = this;

    self.$fields = self.$el.find("input").addClass("typesuggest-input").attr('autocomplete', 'off');
    self.$suggest = $('
').appendTo(self.$el);
  }

  TypeSuggest.prototype.events = function(){
    var self = this;

    self.$fields.on('keydown', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      // if up or down arrows, scroll thru list. if enter key, select item
      if(key == 40 || key == 38 || key == 13){

        if(e.keyCode == 40){ // down arrow

          self.$suggest.addClass("selecting");
          self.move(+1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 38){ // up arrow

          self.$suggest.addClass("selecting");
          self.move(-1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 13){ // enter

          var $selectedItem = self.$suggest.find(".selected");
          var data = $selectedItem.data();

          self.selectItem(data);

          // prevent caret from moving
          return false;

        }else{ // everything else

          self.$suggest.removeClass("selecting");

        }

      }

    })

    self.$fields.on('keyup', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      self.$currentField = $field;

      // if esc key, hide suggestions
      if(key === 27){
        self.updateList(true);
      }

      // if not up or down arrows or enter key, fill suggestion
      if(key !== 40 && key !== 38 && key !== 13 && key !== 27){

        if(!self.debounce){

          // get value
          var val = $field.val();

          self.options = [];

          if(val.length && val.length >= self.config.min_characters){

            self.startDebounce();

            if(self.config.ajax){
              if(self.config.beforeSend){
                val = self.config.beforeSend(val);
              }

              if(self.config.url.indexOf('?') > -1){
                var url = self.config.url + "&" + self.config.param + "=" + val;
              }else{
                var url = self.config.url + "?" + self.config.param + "=" + val;
              }

              $.ajax({
                url: url,
                type: 'GET',
                dataType: 'json',
                success: function(data, textStatus, xhr){
                  self.options = data[self.config.results_key];

                  self.updateList();
                }
              })
            }else{
              // compare value to provided list
              $.each(self.data, function(i, item){
                if(self.config.ignored_key && !item[self.config.ignored_key]){
                  var valLower = val.toLowerCase();
                  var itemLower;

                  if(self.config.search_keys){
                    $.each(self.config.search_keys, function(i, search_key){

                      // type check search keys, checking for string or array (object)
                      if(typeof item[search_key] === 'string'){
                        itemLower = item[search_key].toLowerCase();
                      }

                      if(typeof item[search_key] === 'object' && item[search_key].length){
                        $.each(item[search_key], function(i, keyword){
                          itemLower = keyword.toLowerCase();
                        })
                      }
                    })
                  }else{
                    itemLower = item.toLowerCase();
                  }

                  if(itemLower.search(valLower) > -1){
                    self.options.push(item);
                  }
                }
              })

            }

          }

          self.updateList();
        }
      }else{
        // prevent caret from moving
        return false;
      }
    })

    self.$suggest.on('click', 'li', function(){
      var data = $(this).data();

      self.selectItem(data);
      self.config.callback && self.config.callback(data);
    })

    self.$suggest.on('mouseover', 'li', function(){
      var $li = $(this);

      self.$suggest.find("li").removeClass("selected");
      $li.addClass("selected");
    })

    // if click outside the box, hide suggestions
    $('html').on('click', function(){
      self.updateList(true);
    })
  }

  TypeSuggest.prototype.move = function(where){
    var self = this;
    var $selectedItem = self.$suggest.find(".selected");
    var firstSelected = self.$suggest.find("li:first").hasClass("selected");
    var lastSelected = self.$suggest.find("li:last").hasClass("selected");

    if($selectedItem.length && where > 0){

      if(lastSelected){
        // select first
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:first").addClass("selected");
      }else{
        // selection exists, go to next
        $selectedItem.removeClass("selected").next("li").addClass("selected")
      }

    }
    else if($selectedItem.length && where < 0){

      if(firstSelected){
        // select last
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:last").addClass("selected");
      }else{
        // selection exists, go to prev
        $selectedItem.removeClass("selected").prev("li").addClass("selected")
      }

    }
    else{

      // select first
      self.$suggest.find("li:first").addClass("selected");

    }
  }

  TypeSuggest.prototype.updateList = function(clear){
    var self = this;
    var $list = $('
');

    if(self.options && self.options.length && !clear){
      self.$suggest.addClass("selecting");

      var options = self.options;
      $.each(options, function(i, option){
        var $listItem = $("
").data("option", option);

        if(self.config.display_keys){
          var output = '';

          $.each(self.config.display_keys, function(i, key){
            output += '' + option[key] + ''
          })

          $listItem.html(output);
        }else{
          $listItem.html(option);
        }

        $list.append($listItem);
      })

      self.$suggest.html($list);

    }else{
      self.$suggest.removeClass("selecting").empty();;
    }
  }

  TypeSuggest.prototype.selectItem = function(data){
    var self = this;

    self.options = [];
    self.updateList(true);

    if(!self.config.display_keys){
      self.$currentField.val(data.option);
    }

    self.config.callback && self.config.callback(data);
  }

  TypeSuggest.prototype.startDebounce = function(){
    var self = this;

    self.debounce = true;

    setTimeout(function(){
      self.debounce = false;
    }, 250)
  }

  $.fn.suggest = function(data, options){
    var suggest = new TypeSuggest(this, data, options);
    return this;
  }

})(jQuery);<|endoftext|>


<commit_before>/*
Language: Augmented Backus-Naur Form
Author: Alex McKibben <alex@nullscope.net>
*/

function(hljs) {
    var regexes = {
        ruleDeclaration: "^[a-zA-Z][a-zA-Z0-9-]*",
        ruleReference: "[a-zA-Z][a-zA-Z0-9-]*",
        unexpectedChars: "[!@#$^&',?+~`|:]"
    };

    var keywords = [
        "ALPHA",
        "BIT",
        "CHAR",
        "CR",
        "CRLF",
        "CTL",
        "DIGIT",
        "DQUOTE",
        "HEXDIG",
        "HTAB",
        "LF",
        "LWSP",
        "OCTET",
        "SP",
        "VCHAR",
        "WSP"
    ];

    var keywordMode = {
        beginKeywords: keywords.join(" ")
    };

    var commentMode = hljs.COMMENT(";", "$");

    var terminalBinaryMode = {
        className: "symbol",
        begin: /%b[0-1]+(-[0-1]+|(\.[0-1]+)+){0,1}/
    };

    var terminalDecimalMode = {
        className: "symbol",
        begin: /%d[0-9]+(-[0-9]+|(\.[0-9]+)+){0,1}/
    };

    var terminalHexadecimalMode = {
        className: "symbol",
        begin: /%x[0-9A-F]+(-[0-9A-F]+|(\.[0-9A-F]+)+){0,1}/,
    };

    var caseSensitivityIndicatorMode = {
        className: "symbol",
        begin: /%[si]/
    };

    var ruleReferenceMode = {
        className: "type",
        begin: regexes.ruleReference
    };

    var ruleDeclarationMode = {
        begin: regexes.ruleDeclaration + '\\s*=',
        returnBegin: true,
        end: /=/,
        relevance: 0,
        contains: [{className: "attribute", begin: regexes.ruleDeclaration}]
    };

    return {
      illegal: regexes.unexpectedChars,
      contains: [
          ruleDeclarationMode,
          commentMode,
          keywordMode,
          ruleReferenceMode,
          terminalBinaryMode,
          terminalDecimalMode,
          terminalHexadecimalMode,
          caseSensitivityIndicatorMode,
          hljs.QUOTE_STRING_MODE,
          hljs.NUMBER_MODE
      ]
    };
}
<commit_msg>Remove `ruleReferenceMode` from abnf.js<commit_after>/*
Language: Augmented Backus-Naur Form
Author: Alex McKibben <alex@nullscope.net>
*/

function(hljs) {
    var regexes = {
        ruleDeclaration: "^[a-zA-Z][a-zA-Z0-9-]*",
        unexpectedChars: "[!@#$^&',?+~`|:]"
    };

    var keywords = [
        "ALPHA",
        "BIT",
        "CHAR",
        "CR",
        "CRLF",
        "CTL",
        "DIGIT",
        "DQUOTE",
        "HEXDIG",
        "HTAB",
        "LF",
        "LWSP",
        "OCTET",
        "SP",
        "VCHAR",
        "WSP"
    ];

    var keywordMode = {
        beginKeywords: keywords.join(" ")
    };

    var commentMode = hljs.COMMENT(";", "$");

    var terminalBinaryMode = {
        className: "symbol",
        begin: /%b[0-1]+(-[0-1]+|(\.[0-1]+)+){0,1}/
    };

    var terminalDecimalMode = {
        className: "symbol",
        begin: /%d[0-9]+(-[0-9]+|(\.[0-9]+)+){0,1}/
    };

    var terminalHexadecimalMode = {
        className: "symbol",
        begin: /%x[0-9A-F]+(-[0-9A-F]+|(\.[0-9A-F]+)+){0,1}/,
    };

    var caseSensitivityIndicatorMode = {
        className: "symbol",
        begin: /%[si]/
    };

    var ruleDeclarationMode = {
        begin: regexes.ruleDeclaration + '\\s*=',
        returnBegin: true,
        end: /=/,
        relevance: 0,
        contains: [{className: "attribute", begin: regexes.ruleDeclaration}]
    };

    return {
      illegal: regexes.unexpectedChars,
      contains: [
          ruleDeclarationMode,
          commentMode,
          keywordMode,
          terminalBinaryMode,
          terminalDecimalMode,
          terminalHexadecimalMode,
          caseSensitivityIndicatorMode,
          hljs.QUOTE_STRING_MODE,
          hljs.NUMBER_MODE
      ]
    };
}
<|endoftext|>


<commit_before>import Config from '~/core/config';
import Event from '~/helpers/Event';
import Error from '~/helpers/Error';
import { nth, omit, tail, without } from 'lodash';

const INITIALIZE_CHAT = 'INITIALIZE_CHAT';
const RESET_CHAT = 'RESET_CHAT';

const JOIN_CHANNEL = 'JOIN_CHANNEL';
const LEAVE_CHANNEL = 'LEAVE_CHANNEL';
const SWITCH_CHANNEL = 'SWITCH_CHANNEL';

const SEND_MESSAGE_PENDING = 'SEND_MESSAGE_PENDING';
const SEND_MESSAGE_SUCCESS = 'SEND_MESSAGE_SUCCESS';
const SEND_MESSAGE_ERROR = 'SEND_MESSAGE_ERROR';

const EVENT = 'EVENT';

const initialState = {
  activeChannel: Config.idleChannel,
  initialized: false,
  channels: {
    [Config.idleChannel]: {
      idle: true,
      users: [],
      events: [],
      viewed: true,
    },
  },
  error: null,
  pendingMessage: false,
};

export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case INITIALIZE_CHAT:
      return Object.assign({}, state, {
        initialized: true,
      });

    case JOIN_CHANNEL:
      return Object.assign({}, state, {
        activeChannel: action.channel,
        channels: Object.assign({}, state.channels, {
          [action.channel]: {
            idle: false,
            users: [],
            events: action.events,
            viewed: true,
          },
        }),
      });

    case LEAVE_CHANNEL:
      return Object.assign({}, state, {
        activeChannel: action.activeChannel,
        channels: omit(state.channels, action.channel),
      });

    case SEND_MESSAGE_PENDING:
      return Object.assign({}, state, {
        pendingMessage: true,
      });

    case SEND_MESSAGE_SUCCESS:
      return Object.assign({}, state, {
        pendingMessage: false,
      });

    case SEND_MESSAGE_ERROR:
      return Object.assign({}, state, {
        pendingMessage: false,
        error: action.error,
      });

    case EVENT:
    case SWITCH_CHANNEL:
      return Object.assign({}, state, action.payload);

    case RESET_CHAT:
      return initialState;

    default:
      return state;
  }
}

export function emitEvent(event = {}) {
  return (dispatch, getState) => {
    const { chat } = getState();
    const { activeChannel, channels } = chat;
    const data = event.data;
    const channel = data.channel || activeChannel;
    const c = channels[channel];
    let payload = {};

    switch (event.action) {
      case 'join':
      case 'leave':
        payload = {
          channels: Object.assign({}, channels, {
            [channel]: {
              ...c,
              viewed: activeChannel === channel,
              events: [...c.events, data],
              users: data.users,
            },
          }),
        };
        break;

      case 'event':
      case 'message':
        payload = {
          channels: Object.assign({}, channels, {
            [channel]: {
              ...c,
              viewed: activeChannel === channel,
              events: [...c.events, data],
            },
          }),
        };
        break;

      default:
        payload = event;
        break;
    }

    dispatch({
      type: EVENT,
      payload,
    });
  };
}

export function switchChannel(channel) {
  return (dispatch, getState) => {
    const { channels } = getState().chat;
    const c = channels[channel];

    dispatch({
      type: SWITCH_CHANNEL,
      payload: {
        activeChannel: channel,
        channels: Object.assign({}, channels, {
          [channel]: {
            ...c,
            viewed: true,
          },
        }),
      },
    });
  };
}

export function joinChannel(channel, rejoin = false) {
  return (dispatch, getState) => {
    const store = getState();
    const { socket } = store.primus;
    const c = store.chat.channels[channel];

    if (c && !rejoin) {
      dispatch(switchChannel(channel));
    } else if (!c || rejoin) {
      socket.writeAndWait({
        action: 'join-channel',
        data: { channel },
      }, (err) => {
        if (err) {
          dispatch(emitEvent(new Error(err)));
        } else {
          dispatch({
            type: JOIN_CHANNEL,
            channel,
            events: (c) ? c.events : [],
          });
        }
      });
    }
  };
}

export function leaveChannel(channel) {
  return (dispatch, getState) => {
    const store = getState();
    const { socket } = store.primus;
    const { channels } = store.chat;
    const c = channels[channel];
    const keys = Object.keys(channels);
    const nextChannel = keys.indexOf(channel) - 1;
    let activeChannel = tail(without(keys, channel));

    if (nextChannel > -1) {
      activeChannel = nth(keys, nextChannel);
    }

    if (c) {
      socket.writeAndWait({
        action: 'leave-channel',
        data: { channel },
      }, (err) => {
        if (err) {
          dispatch(emitEvent(new Error(err)));
        } else {
          dispatch({
            type: LEAVE_CHANNEL,
            activeChannel,
            channel,
          });
        }
      });
    }
  };
}

export function rejoinChannels() {
  return (dispatch, getState) => {
    const { channels } = getState().chat;

    Object.keys(channels).forEach((channel) => {
      if (!channels[channel].idle) {
        dispatch(joinChannel(channel, true));
      }
    });
  };
}

function sendMessagePending() {
  return {
    type: SEND_MESSAGE_PENDING,
  };
}

function sendMessageSuccess() {
  return {
    type: SEND_MESSAGE_SUCCESS,
  };
}

function sendMessageError(error) {
  return {
    type: SEND_MESSAGE_ERROR,
    error,
  };
}

export function resetChat() {
  return {
    type: RESET_CHAT,
  };
}

export function sendMessage(message, callback = () => {}) {
  return (dispatch, getState) => {
    const store = getState();
    const { socket } = store.primus;
    const { activeChannel } = store.chat;
    dispatch(sendMessagePending());

    socket.writeAndWait({
      action: 'send-message',
      data: {
        channel: activeChannel,
        message,
      },
    }, (err) => {
      if (err) {
        dispatch(sendMessageError(err));
        dispatch(emitEvent(new Error(err)));
      } else {
        dispatch(sendMessageSuccess());
      }

      callback(err);
    });
  };
}

export function initializeChat() {
  return (dispatch, getState) => {
    const { socket } = getState().primus;

    dispatch({
      type: INITIALIZE_CHAT,
    });

    socket.on('data', (data) => dispatch(emitEvent(data)));
    socket.on('reconnect', () => dispatch(emitEvent(new Event('reconnecting'))));
    socket.on('reconnected', () => dispatch(emitEvent(new Event('connected'))));
    socket.on('reconnect failed', () => dispatch(emitEvent(new Event('reconnect failed'))));
  };
}
<commit_msg>add quit event handling to chat redux module<commit_after>import Config from '~/core/config';
import Event from '~/helpers/Event';
import Error from '~/helpers/Error';
import { nth, omit, tail, without } from 'lodash';

const INITIALIZE_CHAT = 'INITIALIZE_CHAT';
const RESET_CHAT = 'RESET_CHAT';

const JOIN_CHANNEL = 'JOIN_CHANNEL';
const LEAVE_CHANNEL = 'LEAVE_CHANNEL';
const SWITCH_CHANNEL = 'SWITCH_CHANNEL';

const SEND_MESSAGE_PENDING = 'SEND_MESSAGE_PENDING';
const SEND_MESSAGE_SUCCESS = 'SEND_MESSAGE_SUCCESS';
const SEND_MESSAGE_ERROR = 'SEND_MESSAGE_ERROR';

const EVENT = 'EVENT';

const initialState = {
  activeChannel: Config.idleChannel,
  initialized: false,
  channels: {
    [Config.idleChannel]: {
      idle: true,
      users: [],
      events: [],
      viewed: true,
    },
  },
  error: null,
  pendingMessage: false,
};

export default function reducer(state = initialState, action = {}) {
  switch (action.type) {
    case INITIALIZE_CHAT:
      return Object.assign({}, state, {
        initialized: true,
      });

    case JOIN_CHANNEL:
      return Object.assign({}, state, {
        activeChannel: action.channel,
        channels: Object.assign({}, state.channels, {
          [action.channel]: {
            idle: false,
            users: [],
            events: action.events,
            viewed: true,
          },
        }),
      });

    case LEAVE_CHANNEL:
      return Object.assign({}, state, {
        activeChannel: action.activeChannel,
        channels: omit(state.channels, action.channel),
      });

    case SEND_MESSAGE_PENDING:
      return Object.assign({}, state, {
        pendingMessage: true,
      });

    case SEND_MESSAGE_SUCCESS:
      return Object.assign({}, state, {
        pendingMessage: false,
      });

    case SEND_MESSAGE_ERROR:
      return Object.assign({}, state, {
        pendingMessage: false,
        error: action.error,
      });

    case EVENT:
    case SWITCH_CHANNEL:
      return Object.assign({}, state, action.payload);

    case RESET_CHAT:
      return initialState;

    default:
      return state;
  }
}

export function emitEvent(event = {}) {
  return (dispatch, getState) => {
    const { chat } = getState();
    const { activeChannel, channels } = chat;
    const data = event.data;
    const channel = data.channel || activeChannel;
    let c = channels[channel];
    let payload = {};

    switch (event.action) {
      case 'join':
      case 'leave':
        payload = {
          channels: Object.assign({}, channels, {
            [channel]: {
              ...c,
              events: [...c.events, data],
              users: data.users,
              viewed: (data.silent) ? c.viewed : activeChannel === channel,
            },
          }),
        };
        break;

      case 'quit':
        payload = {
          channels,
        };

        data.channels.forEach((chan) => {
          c = channels[chan];

          if (c) {
            const isInAC = channels[activeChannel].users.indexOf(data.username) !== -1;

            payload.channels = Object.assign({}, payload.channels, {
              [chan]: {
                ...c,
                events: [...c.events, data],
                viewed: (!isInAC) ? false : c.viewed,
              },
            });
          }
        });
        break;

      case 'event':
      case 'message':
        payload = {
          channels: Object.assign({}, channels, {
            [channel]: {
              ...c,
              events: [...c.events, data],
              viewed: (data.silent) ? c.viewed : activeChannel === channel,
            },
          }),
        };
        break;

      default:
        payload = event;
        break;
    }

    dispatch({
      type: EVENT,
      payload,
    });
  };
}

export function switchChannel(channel) {
  return (dispatch, getState) => {
    const { channels } = getState().chat;
    const c = channels[channel];

    dispatch({
      type: SWITCH_CHANNEL,
      payload: {
        activeChannel: channel,
        channels: Object.assign({}, channels, {
          [channel]: {
            ...c,
            viewed: true,
          },
        }),
      },
    });
  };
}

export function joinChannel(channel, rejoin = false) {
  return (dispatch, getState) => {
    const store = getState();
    const { socket } = store.primus;
    const c = store.chat.channels[channel];

    if (c && !rejoin) {
      dispatch(switchChannel(channel));
    } else if (!c || rejoin) {
      socket.writeAndWait({
        action: 'join-channel',
        data: { channel },
      }, (err) => {
        if (err) {
          dispatch(emitEvent(new Error(err)));
        } else {
          dispatch({
            type: JOIN_CHANNEL,
            channel,
            events: (c) ? c.events : [],
          });
        }
      });
    }
  };
}

export function leaveChannel(channel) {
  return (dispatch, getState) => {
    const store = getState();
    const { socket } = store.primus;
    const { channels } = store.chat;
    const c = channels[channel];
    const keys = Object.keys(channels);
    const nextChannel = keys.indexOf(channel) - 1;
    let activeChannel = tail(without(keys, channel));

    if (nextChannel > -1) {
      activeChannel = nth(keys, nextChannel);
    }

    if (c) {
      socket.writeAndWait({
        action: 'leave-channel',
        data: { channel },
      }, (err) => {
        if (err) {
          dispatch(emitEvent(new Error(err)));
        } else {
          dispatch({
            type: LEAVE_CHANNEL,
            activeChannel,
            channel,
          });
        }
      });
    }
  };
}

export function rejoinChannels() {
  return (dispatch, getState) => {
    const { channels } = getState().chat;

    Object.keys(channels).forEach((channel) => {
      if (!channels[channel].idle) {
        dispatch(joinChannel(channel, true));
      }
    });
  };
}

function sendMessagePending() {
  return {
    type: SEND_MESSAGE_PENDING,
  };
}

function sendMessageSuccess() {
  return {
    type: SEND_MESSAGE_SUCCESS,
  };
}

function sendMessageError(error) {
  return {
    type: SEND_MESSAGE_ERROR,
    error,
  };
}

export function resetChat() {
  return {
    type: RESET_CHAT,
  };
}

export function sendMessage(message, callback = () => {}) {
  return (dispatch, getState) => {
    const store = getState();
    const { socket } = store.primus;
    const { activeChannel } = store.chat;
    dispatch(sendMessagePending());

    socket.writeAndWait({
      action: 'send-message',
      data: {
        channel: activeChannel,
        message,
      },
    }, (err) => {
      if (err) {
        dispatch(sendMessageError(err));
        dispatch(emitEvent(new Error(err)));
      } else {
        dispatch(sendMessageSuccess());
      }

      callback(err);
    });
  };
}

export function initializeChat() {
  return (dispatch, getState) => {
    const { socket } = getState().primus;

    dispatch({
      type: INITIALIZE_CHAT,
    });

    socket.on('open', () => dispatch(emitEvent(new Event('connected'))));
    socket.on('data', (data) => dispatch(emitEvent(data)));
    socket.on('reconnect', () => dispatch(emitEvent(new Event('reconnecting'))));
    socket.on('reconnect failed', () => dispatch(emitEvent(new Event('reconnect failed'))));
  };
}
<|endoftext|>


<commit_before>var Github = {
	initialize: function () {
		this.fetch();
	},
	fetch: function() {
		$.getJSON('http://github.com/api/v1/json/pacoguzman?callback=?', Github.load);
	},
	load: function(github_data) {
		var gh = Github;
		var repositories = github_data.user.repositories;
		repositories.sort(function(a,b) {
			return (a.watchers + a.forks) - (b.watchers + b.forks);
		});
		$('.project_holder').hide();
		$.each(repositories, function(i, repository) {
			if (!repository.fork && !repository.private) {
				if (repository.watchers > 2) {
					// popular
					gh.displayRepository('popular', repository, true);
				} else {
					// other
					gh.displayRepository('other', repository, false);
				}
			}
		});
	},
	displayRepository: function(inside, repo, show_stats) {
		var repo_html = '

' + repo.name + '';
		if (show_stats) {
			repo_html += ' (' + repo.watchers + ')';
		}
		repo_html += '
';
		repo_html += '
' + repo.description + '

';
		$('#projects_' + inside).prepend(repo_html);
	}
};

$(function() {
	Github.initialize();
})
<commit_msg>Added forked repos<commit_after>var Github = {
	initialize: function () {
		this.fetch();
	},
	fetch: function() {
		$.getJSON('http://github.com/api/v1/json/pacoguzman?callback=?', Github.load);
	},
	load: function(github_data) {
		var gh = Github;
		var repositories = github_data.user.repositories;
		repositories.sort(function(a,b) {
			return (a.watchers + a.forks) - (b.watchers + b.forks);
		});
		$('.project_holder').hide();
		$.each(repositories, function(i, repository) {
			if (!repository.fork && !repository.private) {
				if (repository.watchers > 2) {
					// popular
					gh.displayRepository('popular', repository, true);
				} else {
					// other
					gh.displayRepository('other', repository, false);
				}
			}
      if (repository.fork && !repository.private) {
        gh.displayRepository('forks', repository, false);
      }
		});
	},
	displayRepository: function(inside, repo, show_stats) {
		var repo_html = '

' + repo.name + '';
		if (show_stats) {
			repo_html += ' (' + repo.watchers + ')';
		}
		repo_html += '
';
		repo_html += '
' + repo.description + '

';
		$('#projects_' + inside).prepend(repo_html);
	}
};

$(function() {
	Github.initialize();
})
<|endoftext|>


<commit_before>Function.prototype.once = function once(period, context, args) {
	var now = (new Date()).getTime();
	if (!this.__once__) {
		Object.defineProperty(this, '__once__', {
			value: {
				fn: this,
				time: -1,
				context: null,
				args: null,
				timeout: null,
				period: -1,
				promise: null,
			},
		});
	}
	var once = this.__once__;
	once.context = context;
	once.args = Array.prototype.slice.call(arguments, 2);
	if (!once.timeout || once.period <= 0 || once.period > period) {
		once.period = period;
		once.promise = new Promise(function (resolve) {
			once.timeout && clearTimeout(once.timeout);
			once.timeout = setTimeout(function () {
				once.timeout = null;
				resolve(once.fn.apply(once.context, once.args));
			}, once.period);
		});
		once.time = now;
	}

	return once.promise;
};
<commit_msg>Add function.abort method to cancel delayed once-call; refactor a bit<commit_after>Function.prototype.once = function once(period, context, args) {
	var now = (new Date()).getTime();
	if (typeof this != 'function')
		throw new Error('Function.once() should be called within a function scope');
	if (!this.__once__) {
		Object.defineProperty(this, '__once__', {
			value: {
				fn: this,
				time: -1,
				context: null,
				args: null,
				timeout: null,
				period: -1,
				promise: null,
				abort: false,
				abortReason: null,
			},
		});
	}
	var once = this.__once__;
	once.context = context;
	once.args = Array.prototype.slice.call(arguments, 2);
	if (!once.timeout || period < once.period || once.period <= 0) {
		once.period = period;
		once.promise = new Promise(function (resolve, reject) {
			once.timeout && clearTimeout(once.timeout);
			once.timeout = setTimeout(function () {
				once.timeout = null;
				if (!once.abort) {
					try {
						resolve(once.fn.apply(once.context, once.args));
					}
					catch (e) {
						reject(e);
					}
				}
				else if (once.abortReason)
					reject(once.abortReason);
			}, once.period);
		});
		once.time = now;
	}

	return once.promise;
};

Function.prototype.abort = function abort(reason) {
	if (!this.__once__ || !this.__once__.timeout)
		return false;
	this.__once__.abort = true;
	this.__once__.abortReason = reason;
	return true;
};<|endoftext|>


<commit_before>// http://fajitanachos.com/Authenticating-with-the-GitHub-API/
// Use gatekeeper to log people in
// https://github.com/prose/gatekeeper
//
// Use github.js for other tasks
// https://github.com/michael/github
// other
// http://blog.vjeux.com/2012/javascript/github-oauth-login-browser-side.html

(function(){

var code;
var codeMatch = window.location.href.match(/\?code=(.*)/);
var repoURL = 'https://api.github.com/repos/videojs/video.js';
var categoryLabels = ['enhancement', 'bug', 'question', 'feature'];
var maintainers = ['heff', 'mmcc'];
var token;
var allIssues = [];
var allIssuesByNum = {};
var allComments = [];
var currentUser;
var client;
var gk;

if (window.location.href.indexOf('//localhost') !== -1) {
  client = '53fa7472045a17012bf4';
  gk = 'lhgk';
} else {
  client = '17d41b715e1dd3d5aa04';
  gk = 'cgk';
}

if (codeMatch) {
  code = codeMatch[1];
} else {
  getAuthCode();
  return;
}

$(function(){
  $.getJSON('http://'+gk+'.herokuapp.com/authenticate/'+code, function(data) {

    if (data && data.error == 'bad_code') {
      getAuthCode();
      return;
    }

    token = data.token;
    console.log('Successfully logged in');

    getUser({}, function(user){
      currentUser = user;

      getAllIssues({}, function(allIssues){

        getUncategorizedIssues(function(issues){
          updateIssueColumn('uncategorized', issues);
        });

        getUnconfirmedIssues(function(issues){
          updateIssueColumn('unconfirmed', issues);
        });

        getUnclaimedIssues(function(issues){
          updateIssueColumn('unclaimed', issues);
        });

        getIncompleteIssues(function(issues){
          updateIssueColumn('incomplete', issues);
        });

      });
    });
  });
});

function getAuthCode(){
  location.href = 'https://github.com/login/oauth/authorize?client_id='+client+'&redirect_uri='+encodeURIComponent(window.location.href);
};

function getRepo(options, callback){
  options = _.merge({
    access_token: token
  }, options);

  $.getJSON(repoURL, options, callback);
}

function getRecords(urlPart, options, callback){
  options = _.merge({
    access_token: token,
    sort: 'created',
    direction: 'asc',
    per_page: 100
  }, options);

  console.log('get records', urlPart, options.page);

  $.getJSON(repoURL+urlPart, options, callback);
}

function getAllRecords(urlPart, options, callback){
  var allRecords = [];
  options = options || {};

  getPage(1);
  function getPage(pageNum){
    options.page = pageNum;

    getRecords(urlPart, options, function(records){
      allRecords = allRecords.concat(records);

      if (records.length == 100) {
        getPage(pageNum+1);
      } else {
        callback(allRecords);
      }
    });
  }
}

function getAllIssues(options, callback){
  allIssues = []; // store.get('allIssues')
  allIssuesByNum = {};

  options = _.merge({
    state: 'open'
  }, options);

  // only grab issues after the ones we have
  if (allIssues.length > 0) {
    options.since = allIssues[allIssues.length -1].created_at;
  }

  getAllRecords('/issues', options, function(issues){
    _.each(issues, function(issueData){
      var issue = new Issue(issueData);
      allIssues.push(issue);
    });

    // store.set('allIssues', allIssues);

    // build allIssuesByNum from scratch
    _.each(allIssues, function(issue){
      allIssuesByNum[issue.number] = issue;
    });

    // update all comments and associate them with issues
    getAllComments({}, function(comments){
      _.each(comments, function(comment, i){
        comment.issue_number = parseInt(comment.issue_url.replace(/^\D+/, ''), 10);

        var issue = allIssuesByNum[comment.issue_number];

        // issue may be closed and not exist here
        if (issue) {
          issue.comments.push(comment);
          if (issue.commenters.indexOf(comment.user.login) <= 0) {
            issue.commenters.push(comment.user.login);
          }

        }
      });

      // finish getAllIssues
      callback(allIssues);
    });
  });
}

function getIssuesComments(options, callback){
  options = _.merge({
    per_page: 100,
    sort: 'created',
    direction: 'asc'
    // since: 'YYYY-MM-DDTHH:MM:SSZ'
  }, options);

  $.getJSON(repoURL+'/issues/comments', options, function(comments){
    callback(comments);
  });
}

function getAllComments(options, callback) {
  allComments = [];

  options = _.merge({}, options);
  // only get comments the come after the first open issue
  if (allIssues.length > 0) {
    options.since = allIssues[0].created_at;
  }

  getAllRecords('/issues/comments', options, function(comments){
    allComments = allComments.concat(comments);
    callback(allComments);
  });
}

function getUser(options, callback){
  options = _.merge({
    access_token: token
  }, options);

  $.getJSON('https://api.github.com/user', options, function(user){
    callback(user);
  });
}

function sortByNeedsResponse(issues){
  var needs = [];
  var noNeeds = [];
  issues = issues || allIssues;

  _.each(issues, function(issue){
    if (issue.needsResponse()) {
      needs.push(issue);
    } else {
      noNeeds.push(issue);
    }
  });

  return needs.concat(noNeeds);
}

function sortByCommentersCount(issues){
  issues = issues || allIssues;

  // lodash-fu to sort by commenter number decsending
  return _(issues).sortBy(function(issue){ return issue.commenters.length }).reverse().value();
}

function getUncategorizedIssues(callback){
  var uncategorized = [];

  _.each(allIssues, function(issue){
    if (issue.state == 'created') {
      uncategorized.push(issue);
    }
  });

  callback(sortByNeedsResponse(uncategorized));
}

function getUnconfirmedIssues(callback){
  var unconfirmed = [];

  // check for issues that have nobody assigned to them
  _.each(allIssues, function(issue, i){
    if (issue.state == 'categorized') {
      unconfirmed.push(issue);
    }
  });

  callback(sortByNeedsResponse(unconfirmed));
}

function getUnclaimedIssues(callback){
  var unclaimed = [];

  // check for issues that have nobody assigned to them
  _.each(allIssues, function(issue, i){
    if (issue.state == 'confirmed') {
      unclaimed.push(issue);
    }
  });

  callback(sortByCommentersCount(unclaimed));
}

function getIncompleteIssues(callback){
  var incomplete = [];

  _.each(allIssues, function(issue){
    if (issue.state == 'claimed') {
      incomplete.push(issue);
    }
  });

  callback(sortByNeedsResponse(incomplete));
}

function updateIssueColumn(columnName, issues){
  var html = '';

  _.each(issues, function(issue){
    html += '
';

    if (issue.needsResponse()) {
      html += '* ';
    }

    html += ''+issue.number+' '+issue.title+'
';
  });

  if (!html) {
    html = 'No issues';
  }

  $('#'+columnName+'-issues').html(html);
};

/**
 * Issue Class
 * @param {Object} data Raw issue data
 */
function Issue(data){
  _.merge(this, data);

  // i want to use 'state'
  this.origState = this.state;
  this.state = this.getState();

  // comments should really be comments_count
  this.comments_count = this.comments;
  this.comments = [];
  this.commenters = [];
}

Issue.prototype.getState = function(){
  if (!this.isCategorized()) {
    return this.state = 'created';
  } else if (!this.isConfirmed()) {
    return this.state = 'categorized';
  } else if (!this.isClaimed()) {
    return this.state = 'confirmed';
  } else {
    return this.state = 'claimed';
  }
};

Issue.prototype.isCategorized = function(){
  return !!this.getCategory();
};

Issue.prototype.getCategory = function(){
  if (this.category_ !== undefined) {
    return this.category_;
  }

  this.category_ = false;
  if (this.labels.length > 0) {
    this.category_ = _.find(this.labels, function(label){
      return categoryLabels.indexOf(label.name) >= 0;
    });
  }

  return this.category_;
};

Issue.prototype.isConfirmed = function(){
  if (this.confirmed_ !== undefined) {
    return this.confirmed_;
  }

  this.confirmed_ = this.isCategorized() && _.some(this.labels, function(label){
    return label.name === 'confirmed';
  });

  return this.confirmed_;
};

Issue.prototype.isClaimed = function(){
  if (this.claimed_ !== undefined) {
    return this.claimed_;
  }

  this.claimed_ = this.isConfirmed() && !!(this.assignee || this.pull_request);

  return this.claimed_;
};

Issue.prototype.lastCommentByMaintainer = function(callback){
  var lastCommenter = this.comments[this.comments.length - 1].user.login;

  if (maintainers.indexOf(lastCommenter) >= 0) {
    return true;
  } else {
    return false;
  }
};

Issue.prototype.isMine = function(callback){
  var lastCommenter = this.comments[this.comments.length - 1].user.login;

  if (maintainers.indexOf(lastCommenter) >= 0) {
    return true;
  } else {
    return false;
  }
};

Issue.prototype.isMine = function(){
  return this.user.login === currentUser.login;
};

Issue.prototype.lastComment = function(){
  return this.comments[this.comments.length - 1];
};

/**
 * Situations where an issue needs your response
 * - you submitted it and you are not the last commenter
 * - you are a maintainer and a maintainer is not the last commenter
 */
Issue.prototype.needsResponse = function(callback){
  if (this.isMine()) {
    if (this.lastComment() && this.lastComment().user.login !== currentUser.login) {
      return true;
    } else {
      return false;
    }
  }

  if (maintainers.indexOf(currentUser.login) >= 0) {
    if (this.comments_count == 0) {
      if (!this.confirmed_) {
        // if there's no comments on an unconfirmed issue, it needs a response
        return true;
      } else {
        // it could be a confirmed issue from a maintainer, no comments
        return !(maintainers.indexOf(this.user.login) >= 0);
      }
    }

    // if (!this.getNeeds() || this.getNeeds().length == 0) {
    //   return false;
    // }

    // if the last commenter was 'us', it hasn't been updated yet
    if (maintainers.indexOf(this.lastComment().user.login) >= 0) {
      return false;
    } else {
      return true;
    }
  }
};

Issue.prototype.getNeeds = function(){
  if (this.needs_) {
    return this.needs_;
  }

  this.needs_ = [];

  var issue = this;
  _.each(this.labels, function(label){
    if (label.name.indexOf('needs:') >= 0) {
      issue.needs_.push(label.name.replace(/needs:\s?/, ''));
    }
  });

  return this.needs_;
};

Issue.prototype.getComments = function(callback){
  if (this.commentList_) {
    return callback(issue.commentList_);
  }

  if (this.comments_count == 0) {
    this.commentList_ = [];
    return callback(this.commentList_);
  }

  // options = _.merge({
  //   // sort: 'created',
  //   // direction: 'desc',
  //   // since: 'YYYY-MM-DDTHH:MM:SSZ'
  // }, options);

  $.getJSON(repoURL+'/issues/'+this.number+'/comments', {}, function(comments){
    this.commentList_ = comments;
    callback(this.commentList_);
  });
};

})();<commit_msg>Added repo query var option<commit_after>// http://fajitanachos.com/Authenticating-with-the-GitHub-API/
// Use gatekeeper to log people in
// https://github.com/prose/gatekeeper
//
// Use github.js for other tasks
// https://github.com/michael/github
// other
// http://blog.vjeux.com/2012/javascript/github-oauth-login-browser-side.html

(function(){

var code;
var categoryLabels = ['enhancement', 'bug', 'question', 'feature'];
var maintainers = ['heff', 'mmcc'];
var token;
var allIssues = [];
var allIssuesByNum = {};
var allComments = [];
var currentUser;
var client;
var gk;

var queryVars = {};
window.location.search.substring(1).split('&').forEach(function(str){
  if (!str) return;
  str = str.split('=');
  queryVars[str[0]] = str[1];
});

var codeMatch = window.location.href.match(/\?code=(.*)/);
var repoName = queryVars.repo || 'videojs/video.js';
var repoURL = 'https://api.github.com/repos/'+decodeURIComponent(repoName);

if (window.location.href.indexOf('//localhost') !== -1) {
  client = '53fa7472045a17012bf4';
  gk = 'lhgk';
} else {
  client = '17d41b715e1dd3d5aa04';
  gk = 'cgk';
}

if (queryVars.code) {
  code = queryVars.code;
} else {
  getAuthCode();
  return;
}

$(function(){
  $.getJSON('http://'+gk+'.herokuapp.com/authenticate/'+code, function(data) {

    if (data && data.error == 'bad_code') {
      getAuthCode();
      return;
    }

    token = data.token;
    console.log('Successfully logged in');

    getUser({}, function(user){
      currentUser = user;

      getAllIssues({}, function(allIssues){

        getUncategorizedIssues(function(issues){
          updateIssueColumn('uncategorized', issues);
        });

        getUnconfirmedIssues(function(issues){
          updateIssueColumn('unconfirmed', issues);
        });

        getUnclaimedIssues(function(issues){
          updateIssueColumn('unclaimed', issues);
        });

        getIncompleteIssues(function(issues){
          updateIssueColumn('incomplete', issues);
        });

      });
    });
  });
});

function getAuthCode(){
  location.href = 'https://github.com/login/oauth/authorize?client_id='+client+'&redirect_uri='+encodeURIComponent(window.location.href);
};

function getRepo(options, callback){
  options = _.merge({
    access_token: token
  }, options);

  $.getJSON(repoURL, options, callback);
}

function getRecords(urlPart, options, callback){
  options = _.merge({
    access_token: token,
    sort: 'created',
    direction: 'asc',
    per_page: 100
  }, options);

  console.log('get records', urlPart, options.page);

  $.getJSON(repoURL+urlPart, options, callback);
}

function getAllRecords(urlPart, options, callback){
  var allRecords = [];
  options = options || {};

  getPage(1);
  function getPage(pageNum){
    options.page = pageNum;

    getRecords(urlPart, options, function(records){
      allRecords = allRecords.concat(records);

      if (records.length == 100) {
        getPage(pageNum+1);
      } else {
        callback(allRecords);
      }
    });
  }
}

function getAllIssues(options, callback){
  allIssues = []; // store.get('allIssues')
  allIssuesByNum = {};

  options = _.merge({
    state: 'open'
  }, options);

  // only grab issues after the ones we have
  if (allIssues.length > 0) {
    options.since = allIssues[allIssues.length -1].created_at;
  }

  getAllRecords('/issues', options, function(issues){
    _.each(issues, function(issueData){
      var issue = new Issue(issueData);
      allIssues.push(issue);
    });

    // store.set('allIssues', allIssues);

    // build allIssuesByNum from scratch
    _.each(allIssues, function(issue){
      allIssuesByNum[issue.number] = issue;
    });

    // update all comments and associate them with issues
    getAllComments({}, function(comments){
      _.each(comments, function(comment, i){
        comment.issue_number = parseInt(comment.issue_url.replace(/^\D+/, ''), 10);

        var issue = allIssuesByNum[comment.issue_number];

        // issue may be closed and not exist here
        if (issue) {
          issue.comments.push(comment);
          if (issue.commenters.indexOf(comment.user.login) <= 0) {
            issue.commenters.push(comment.user.login);
          }

        }
      });

      // finish getAllIssues
      callback(allIssues);
    });
  });
}

function getIssuesComments(options, callback){
  options = _.merge({
    per_page: 100,
    sort: 'created',
    direction: 'asc'
    // since: 'YYYY-MM-DDTHH:MM:SSZ'
  }, options);

  $.getJSON(repoURL+'/issues/comments', options, function(comments){
    callback(comments);
  });
}

function getAllComments(options, callback) {
  allComments = [];

  options = _.merge({}, options);
  // only get comments the come after the first open issue
  if (allIssues.length > 0) {
    options.since = allIssues[0].created_at;
  }

  getAllRecords('/issues/comments', options, function(comments){
    allComments = allComments.concat(comments);
    callback(allComments);
  });
}

function getUser(options, callback){
  options = _.merge({
    access_token: token
  }, options);

  $.getJSON('https://api.github.com/user', options, function(user){
    callback(user);
  });
}

function sortByNeedsResponse(issues){
  var needs = [];
  var noNeeds = [];
  issues = issues || allIssues;

  _.each(issues, function(issue){
    if (issue.needsResponse()) {
      needs.push(issue);
    } else {
      noNeeds.push(issue);
    }
  });

  return needs.concat(noNeeds);
}

function sortByCommentersCount(issues){
  issues = issues || allIssues;

  // lodash-fu to sort by commenter number decsending
  return _(issues).sortBy(function(issue){ return issue.commenters.length }).reverse().value();
}

function getUncategorizedIssues(callback){
  var uncategorized = [];

  _.each(allIssues, function(issue){
    if (issue.state == 'created') {
      uncategorized.push(issue);
    }
  });

  callback(sortByNeedsResponse(uncategorized));
}

function getUnconfirmedIssues(callback){
  var unconfirmed = [];

  // check for issues that have nobody assigned to them
  _.each(allIssues, function(issue, i){
    if (issue.state == 'categorized') {
      unconfirmed.push(issue);
    }
  });

  callback(sortByNeedsResponse(unconfirmed));
}

function getUnclaimedIssues(callback){
  var unclaimed = [];

  // check for issues that have nobody assigned to them
  _.each(allIssues, function(issue, i){
    if (issue.state == 'confirmed') {
      unclaimed.push(issue);
    }
  });

  callback(sortByCommentersCount(unclaimed));
}

function getIncompleteIssues(callback){
  var incomplete = [];

  _.each(allIssues, function(issue){
    if (issue.state == 'claimed') {
      incomplete.push(issue);
    }
  });

  callback(sortByNeedsResponse(incomplete));
}

function updateIssueColumn(columnName, issues){
  var html = '';

  _.each(issues, function(issue){
    html += '
';

    if (issue.needsResponse()) {
      html += '* ';
    }

    html += ''+issue.number+' '+issue.title+'
';
  });

  if (!html) {
    html = 'No issues';
  }

  $('#'+columnName+'-issues').html(html);
};

/**
 * Issue Class
 * @param {Object} data Raw issue data
 */
function Issue(data){
  _.merge(this, data);

  // i want to use 'state'
  this.origState = this.state;
  this.state = this.getState();

  // comments should really be comments_count
  this.comments_count = this.comments;
  this.comments = [];
  this.commenters = [];
}

Issue.prototype.getState = function(){
  if (!this.isCategorized()) {
    return this.state = 'created';
  } else if (!this.isConfirmed()) {
    return this.state = 'categorized';
  } else if (!this.isClaimed()) {
    return this.state = 'confirmed';
  } else {
    return this.state = 'claimed';
  }
};

Issue.prototype.isCategorized = function(){
  return !!this.getCategory();
};

Issue.prototype.getCategory = function(){
  if (this.category_ !== undefined) {
    return this.category_;
  }

  this.category_ = false;
  if (this.labels.length > 0) {
    this.category_ = _.find(this.labels, function(label){
      return categoryLabels.indexOf(label.name) >= 0;
    });
  }

  return this.category_;
};

Issue.prototype.isConfirmed = function(){
  if (this.confirmed_ !== undefined) {
    return this.confirmed_;
  }

  this.confirmed_ = this.isCategorized() && _.some(this.labels, function(label){
    return label.name === 'confirmed';
  });

  return this.confirmed_;
};

Issue.prototype.isClaimed = function(){
  if (this.claimed_ !== undefined) {
    return this.claimed_;
  }

  this.claimed_ = this.isConfirmed() && !!(this.assignee || this.pull_request);

  return this.claimed_;
};

Issue.prototype.lastCommentByMaintainer = function(callback){
  var lastCommenter = this.comments[this.comments.length - 1].user.login;

  if (maintainers.indexOf(lastCommenter) >= 0) {
    return true;
  } else {
    return false;
  }
};

Issue.prototype.isMine = function(callback){
  var lastCommenter = this.comments[this.comments.length - 1].user.login;

  if (maintainers.indexOf(lastCommenter) >= 0) {
    return true;
  } else {
    return false;
  }
};

Issue.prototype.isMine = function(){
  return this.user.login === currentUser.login;
};

Issue.prototype.lastComment = function(){
  return this.comments[this.comments.length - 1];
};

/**
 * Situations where an issue needs your response
 * - you submitted it and you are not the last commenter
 * - you are a maintainer and a maintainer is not the last commenter
 */
Issue.prototype.needsResponse = function(callback){
  if (this.isMine()) {
    if (this.lastComment() && this.lastComment().user.login !== currentUser.login) {
      return true;
    } else {
      return false;
    }
  }

  if (maintainers.indexOf(currentUser.login) >= 0) {
    if (this.comments_count == 0) {
      if (!this.confirmed_) {
        // if there's no comments on an unconfirmed issue, it needs a response
        return true;
      } else {
        // it could be a confirmed issue from a maintainer, no comments
        return !(maintainers.indexOf(this.user.login) >= 0);
      }
    }

    // if (!this.getNeeds() || this.getNeeds().length == 0) {
    //   return false;
    // }

    // if the last commenter was 'us', it hasn't been updated yet
    if (maintainers.indexOf(this.lastComment().user.login) >= 0) {
      return false;
    } else {
      return true;
    }
  }
};

Issue.prototype.getNeeds = function(){
  if (this.needs_) {
    return this.needs_;
  }

  this.needs_ = [];

  var issue = this;
  _.each(this.labels, function(label){
    if (label.name.indexOf('needs:') >= 0) {
      issue.needs_.push(label.name.replace(/needs:\s?/, ''));
    }
  });

  return this.needs_;
};

Issue.prototype.getComments = function(callback){
  if (this.commentList_) {
    return callback(issue.commentList_);
  }

  if (this.comments_count == 0) {
    this.commentList_ = [];
    return callback(this.commentList_);
  }

  // options = _.merge({
  //   // sort: 'created',
  //   // direction: 'desc',
  //   // since: 'YYYY-MM-DDTHH:MM:SSZ'
  // }, options);

  $.getJSON(repoURL+'/issues/'+this.number+'/comments', {}, function(comments){
    this.commentList_ = comments;
    callback(this.commentList_);
  });
};

})();<|endoftext|>


<commit_before>import { getTimeoutData, setTimeoutData } from "./lazyload.data";
import { load } from "./lazyload.load";

export const cancelDelayLoad = element => {
    var timeoutId = getTimeoutData(element);
    if (!timeoutId) {
        return; // do nothing if timeout doesn't exist
    }
    clearTimeout(timeoutId);
    setTimeoutData(element, null);
};

export const delayLoad = (element, settings, instance) => {
    var timeoutId = getTimeoutData(element);
    if (timeoutId) {
        return; // do nothing if timeout already set
    }
    timeoutId = setTimeout(function() {
        load(element, settings, instance);
        cancelDelayLoad(element);
    }, settings.load_delay);
    setTimeoutData(element, timeoutId);
};
<commit_msg>Small code readability improvement<commit_after>import { getTimeoutData, setTimeoutData } from "./lazyload.data";
import { load } from "./lazyload.load";

export const cancelDelayLoad = element => {
    var timeoutId = getTimeoutData(element);
    if (!timeoutId) {
        return; // do nothing if timeout doesn't exist
    }
    clearTimeout(timeoutId);
    setTimeoutData(element, null);
};

export const delayLoad = (element, settings, instance) => {
    const loadDelay = settings.load_delay;
    let timeoutId = getTimeoutData(element);
    if (timeoutId) {
        return; // do nothing if timeout already set
    }
    timeoutId = setTimeout(function() {
        load(element, settings, instance);
        cancelDelayLoad(element);
    }, loadDelay);
    setTimeoutData(element, timeoutId);
};
<|endoftext|>


<commit_before>// Copyright 2013-2020, University of Colorado Boulder

/**
 * A DerivedProperty is computed based on other Properties.  This implementation inherits from Property to (a) simplify
 * implementation and (b) ensure it remains consistent. Note that the setters should not be called directly, so the
 * setters (set, reset and es5 setter) throw an error if used directly.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

import merge from '../../phet-core/js/merge.js';
import Tandem from '../../tandem/js/Tandem.js';
import axon from './axon.js';
import DerivedPropertyIO from './DerivedPropertyIO.js';
import Property from './Property.js';

class DerivedProperty extends Property {

  /**
   * @param {Property[]} dependencies - Properties that this Property's value is derived from
   * @param {function} derivation - function that derives this Property's value, expects args in the same order as dependencies
   * @param {Object} [options] - see Property
   */
  constructor( dependencies, derivation, options ) {

    options = merge( {
      tandem: Tandem.OPTIONAL,
      phetioReadOnly: true // derived properties can be read but not set by PhET-iO
    }, options );

    assert && options.tandem.supplied && assert( options.phetioType && options.phetioType.outerType === DerivedPropertyIO,
      'unsupported phetioType' );

    assert && assert( dependencies.length === _.uniq( dependencies ).length, 'duplicate dependencies' );

    const initialValue = derivation.apply( null, dependencies.map( property => property.get() ) );

    // We must pass supertype tandem to parent class so addInstance is called only once in the subclassiest constructor.
    super( initialValue, options );

    if ( this.isPhetioInstrumented() ) {

      // The phetioType should be a concrete (instantiated) DerivedPropertyIO, hence we must check its outer type
      assert && assert( options.phetioType.outerType === DerivedPropertyIO, 'phetioType should be DerivedPropertyIO' );
    }

    this.dependencies = dependencies; // @private

    // We can't reset the DerivedProperty, so we don't store the initial value to help prevent memory issues.
    // See https://github.com/phetsims/axon/issues/193
    this._initialValue = null;

    // @private
    this.derivation = derivation;

    // @private Keep track of listeners so they can be detached
    this.dependencyListeners = [];

    for ( let i = 0; i < dependencies.length; i++ ) {
      const dependency = dependencies[ i ];
      ( dependency => {
        const listener = () => {

          // Just mark that there is a deferred value, then calculate the derivation below when setDeferred() is called.
          // This is in part supported by the PhET-iO state engine because it can account for intermediate states, such
          // that this Property won't notify until after it is undeferred and has taken its final value.
          if ( this.isDeferred ) {
            this.hasDeferredValue = true;
          }
          else {
            super.set( derivation.apply( null, dependencies.map( property => property.get() ) ) );
          }
        };
        this.dependencyListeners.push( listener );
        dependency.lazyLink( listener );

        // Dependencies should have taken their correct values before this DerivedProperty undefers, so it will be sure to have the right value.
        // NOTE: do not mark the beforePhase as NOFITY, as this will potentially cause interdependence bugs when used
        // with Multlinks. See Projectile Motion's use of MeasureingTapeNode for an example.
        Property.registerOrderDependency( dependency, Property.Phase.UNDEFER, this, Property.Phase.UNDEFER );
      } )( dependency, i );
    }
  }

  // @public
  dispose() {

    // Unlink from dependent Properties
    for ( let i = 0; i < this.dependencies.length; i++ ) {
      const dependency = this.dependencies[ i ];
      if ( !dependency.isDisposed ) {
        dependency.unlink( this.dependencyListeners[ i ] );
      }
    }
    this.dependencies = null;
    this.dependencyListeners = null;

    super.dispose( this );
  }

  /**
   * Override the mutators to provide an error message.  These should not be called directly,
   * the value should only be modified when the dependencies change.
   * @param value
   * @override
   * @public
   */
  set( value ) { throw new Error( 'Cannot set values directly to a DerivedProperty, tried to set: ' + value ); }

  /**
   * Override the mutators to provide an error message.  These should not be called directly, the value should only be modified
   * when the dependencies change. Keep the newValue output in the string so the argument won't be stripped by minifier
   * (which would cause crashes like https://github.com/phetsims/axon/issues/15)
   * @param newValue
   * @override
   * @public
   */
  set value( newValue ) { throw new Error( 'Cannot es5-set values directly to a DerivedProperty, tried to set: ' + newValue ); }

  /**
   * Override the mutators to provide an error message.  These should not be called directly,
   * the value should only be modified when the dependencies change.
   * @override
   * @public
   */
  reset() { throw new Error( 'Cannot reset a DerivedProperty directly' ); }

  /**
   * Prevent the retrieval of the initial value, since we don't store it.
   * See https://github.com/phetsims/axon/issues/193
   * @public
   * @override
   * @returns {*}
   */
  getInitialValue() { throw new Error( 'Cannot get the initial value of a DerivedProperty' ); }

  /**
   * Support deferred DerivedProperty by only calculating the derivation once when it is time to undefer it and fire
   * notifications. This way we don't have intermediate derivation calls during PhET-iO state setting.
   * @override
   * @public
   *
   * @param {boolean} isDeferred
   * @returns {function|null}
   */
  setDeferred( isDeferred ) {
    assert && assert( typeof isDeferred === 'boolean' );
    if ( this.isDeferred && !isDeferred ) {
      this.deferredValue = this.derivation.apply( null, this.dependencies.map( property => property.get() ) );
    }
    return super.setDeferred( isDeferred );
  }

  /**
   * Override the getter for value as well, since we need the getter/setter pair to override the getter/setter pair in Property
   * (instead of a setter with no getter overriding). See https://github.com/phetsims/axon/issues/171 for more details
   * @returns {*}
   * @override
   * @public
   */
  get value() { return super.get(); }

  /**
   * Creates a derived boolean Property whose value is true iff firstProperty's value is equal to secondProperty's
   * value.
   * @public
   *
   * @param {Property.<*>} firstProperty
   * @param {Property.<*>} secondProperty
   * @param {Object} [options] - Forwarded to the DerivedProperty
   * @returns {DerivedProperty.<boolean>}
   */
  static valueEquals( firstProperty, secondProperty, options ) {
    return new DerivedProperty( [ firstProperty, secondProperty ], equalsFunction, options );
  }

  /**
   * Creates a derived boolean Property whose value is true iff every input Property value is true.
   * @public
   *
   * @param {Array.<Property.<boolean>>} properties
   * @param {Object} [options] - Forwarded to the DerivedProperty
   * @returns {DerivedProperty.<boolean>}
   */
  static and( properties, options ) {
    return new DerivedProperty( properties, _.reduce.bind( null, properties, andFunction, true ), options );
  }

  /**
   * Creates a derived boolean Property whose value is true iff any input Property value is true.
   * @public
   *
   * @param {Array.<Property.<boolean>>} properties
   * @param {Object} [options] - Forwarded to the DerivedProperty
   * @returns {DerivedProperty.<boolean>}
   */
  static or( properties, options ) {
    return new DerivedProperty( properties, _.reduce.bind( null, properties, orFunction, false ), options );
  }
}

const equalsFunction = ( a, b ) => {
  return a === b;
};

const andFunction = ( value, property ) => {
  assert && assert( typeof property.value === 'boolean', 'boolean value required' );
  return value && property.value;
};

const orFunction = ( value, property ) => {
  assert && assert( typeof property.value === 'boolean', 'boolean value required' );
  return value || property.value;
};

axon.register( 'DerivedProperty', DerivedProperty );
export default DerivedProperty;<commit_msg>fix doc typo<commit_after>// Copyright 2013-2020, University of Colorado Boulder

/**
 * A DerivedProperty is computed based on other Properties.  This implementation inherits from Property to (a) simplify
 * implementation and (b) ensure it remains consistent. Note that the setters should not be called directly, so the
 * setters (set, reset and es5 setter) throw an error if used directly.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

import merge from '../../phet-core/js/merge.js';
import Tandem from '../../tandem/js/Tandem.js';
import axon from './axon.js';
import DerivedPropertyIO from './DerivedPropertyIO.js';
import Property from './Property.js';

class DerivedProperty extends Property {

  /**
   * @param {Property[]} dependencies - Properties that this Property's value is derived from
   * @param {function} derivation - function that derives this Property's value, expects args in the same order as dependencies
   * @param {Object} [options] - see Property
   */
  constructor( dependencies, derivation, options ) {

    options = merge( {
      tandem: Tandem.OPTIONAL,
      phetioReadOnly: true // derived properties can be read but not set by PhET-iO
    }, options );

    assert && options.tandem.supplied && assert( options.phetioType && options.phetioType.outerType === DerivedPropertyIO,
      'unsupported phetioType' );

    assert && assert( dependencies.length === _.uniq( dependencies ).length, 'duplicate dependencies' );

    const initialValue = derivation.apply( null, dependencies.map( property => property.get() ) );

    // We must pass supertype tandem to parent class so addInstance is called only once in the subclassiest constructor.
    super( initialValue, options );

    if ( this.isPhetioInstrumented() ) {

      // The phetioType should be a concrete (instantiated) DerivedPropertyIO, hence we must check its outer type
      assert && assert( options.phetioType.outerType === DerivedPropertyIO, 'phetioType should be DerivedPropertyIO' );
    }

    this.dependencies = dependencies; // @private

    // We can't reset the DerivedProperty, so we don't store the initial value to help prevent memory issues.
    // See https://github.com/phetsims/axon/issues/193
    this._initialValue = null;

    // @private
    this.derivation = derivation;

    // @private Keep track of listeners so they can be detached
    this.dependencyListeners = [];

    for ( let i = 0; i < dependencies.length; i++ ) {
      const dependency = dependencies[ i ];
      ( dependency => {
        const listener = () => {

          // Just mark that there is a deferred value, then calculate the derivation below when setDeferred() is called.
          // This is in part supported by the PhET-iO state engine because it can account for intermediate states, such
          // that this Property won't notify until after it is undeferred and has taken its final value.
          if ( this.isDeferred ) {
            this.hasDeferredValue = true;
          }
          else {
            super.set( derivation.apply( null, dependencies.map( property => property.get() ) ) );
          }
        };
        this.dependencyListeners.push( listener );
        dependency.lazyLink( listener );

        // Dependencies should have taken their correct values before this DerivedProperty undefers, so it will be sure to have the right value.
        // NOTE: do not mark the beforePhase as NOFITY, as this will potentially cause interdependence bugs when used
        // with Multlinks. See Projectile Motion's use of MeasuringTapeNode for an example.
        Property.registerOrderDependency( dependency, Property.Phase.UNDEFER, this, Property.Phase.UNDEFER );
      } )( dependency, i );
    }
  }

  // @public
  dispose() {

    // Unlink from dependent Properties
    for ( let i = 0; i < this.dependencies.length; i++ ) {
      const dependency = this.dependencies[ i ];
      if ( !dependency.isDisposed ) {
        dependency.unlink( this.dependencyListeners[ i ] );
      }
    }
    this.dependencies = null;
    this.dependencyListeners = null;

    super.dispose( this );
  }

  /**
   * Override the mutators to provide an error message.  These should not be called directly,
   * the value should only be modified when the dependencies change.
   * @param value
   * @override
   * @public
   */
  set( value ) { throw new Error( 'Cannot set values directly to a DerivedProperty, tried to set: ' + value ); }

  /**
   * Override the mutators to provide an error message.  These should not be called directly, the value should only be modified
   * when the dependencies change. Keep the newValue output in the string so the argument won't be stripped by minifier
   * (which would cause crashes like https://github.com/phetsims/axon/issues/15)
   * @param newValue
   * @override
   * @public
   */
  set value( newValue ) { throw new Error( 'Cannot es5-set values directly to a DerivedProperty, tried to set: ' + newValue ); }

  /**
   * Override the mutators to provide an error message.  These should not be called directly,
   * the value should only be modified when the dependencies change.
   * @override
   * @public
   */
  reset() { throw new Error( 'Cannot reset a DerivedProperty directly' ); }

  /**
   * Prevent the retrieval of the initial value, since we don't store it.
   * See https://github.com/phetsims/axon/issues/193
   * @public
   * @override
   * @returns {*}
   */
  getInitialValue() { throw new Error( 'Cannot get the initial value of a DerivedProperty' ); }

  /**
   * Support deferred DerivedProperty by only calculating the derivation once when it is time to undefer it and fire
   * notifications. This way we don't have intermediate derivation calls during PhET-iO state setting.
   * @override
   * @public
   *
   * @param {boolean} isDeferred
   * @returns {function|null}
   */
  setDeferred( isDeferred ) {
    assert && assert( typeof isDeferred === 'boolean' );
    if ( this.isDeferred && !isDeferred ) {
      this.deferredValue = this.derivation.apply( null, this.dependencies.map( property => property.get() ) );
    }
    return super.setDeferred( isDeferred );
  }

  /**
   * Override the getter for value as well, since we need the getter/setter pair to override the getter/setter pair in Property
   * (instead of a setter with no getter overriding). See https://github.com/phetsims/axon/issues/171 for more details
   * @returns {*}
   * @override
   * @public
   */
  get value() { return super.get(); }

  /**
   * Creates a derived boolean Property whose value is true iff firstProperty's value is equal to secondProperty's
   * value.
   * @public
   *
   * @param {Property.<*>} firstProperty
   * @param {Property.<*>} secondProperty
   * @param {Object} [options] - Forwarded to the DerivedProperty
   * @returns {DerivedProperty.<boolean>}
   */
  static valueEquals( firstProperty, secondProperty, options ) {
    return new DerivedProperty( [ firstProperty, secondProperty ], equalsFunction, options );
  }

  /**
   * Creates a derived boolean Property whose value is true iff every input Property value is true.
   * @public
   *
   * @param {Array.<Property.<boolean>>} properties
   * @param {Object} [options] - Forwarded to the DerivedProperty
   * @returns {DerivedProperty.<boolean>}
   */
  static and( properties, options ) {
    return new DerivedProperty( properties, _.reduce.bind( null, properties, andFunction, true ), options );
  }

  /**
   * Creates a derived boolean Property whose value is true iff any input Property value is true.
   * @public
   *
   * @param {Array.<Property.<boolean>>} properties
   * @param {Object} [options] - Forwarded to the DerivedProperty
   * @returns {DerivedProperty.<boolean>}
   */
  static or( properties, options ) {
    return new DerivedProperty( properties, _.reduce.bind( null, properties, orFunction, false ), options );
  }
}

const equalsFunction = ( a, b ) => {
  return a === b;
};

const andFunction = ( value, property ) => {
  assert && assert( typeof property.value === 'boolean', 'boolean value required' );
  return value && property.value;
};

const orFunction = ( value, property ) => {
  assert && assert( typeof property.value === 'boolean', 'boolean value required' );
  return value || property.value;
};

axon.register( 'DerivedProperty', DerivedProperty );
export default DerivedProperty;<|endoftext|>


<commit_before>// Function to validate the E-Mail input in the DonationForm

function validateEmail(email) {
    var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(email);
}

var userInput = [];
var DonationForm = document.getElementById("donationForm");

//Function to clear form after valid input

function ClearFields (validateArray){
  if (validateArray.indexOf(false) < 0){
    DonationForm.reset();
  }
}

//Local Storage

function saveStorage(key, UserData) {
  var temp = JSON.stringify(UserData);
  localStorage.setItem(key, temp);
}

function getStorage(key){
  var temp = localStorage.getItem(key);
  return JSON.parse(temp);
}

//Event Listeners for Submit Button

DonationForm.addEventListener("submit", validateDonationForm);
DonationForm.addEventListener("keyup", storeFormValues);

// Function to validate the Name input in the DonationForm

function validateDonationForm(e) {
    e.preventDefault();
    var validateArray = [];
    for (var i = 0; i < e.target.length; i++) {
      var el = e.target[i];
      var InputValue = el.value;
      var InputID = el.id;
      var ElP = el.nextElementSibling;
      var checkEmail = false;
        if (InputID != "SubmitButton" && InputID !=  "DonationAmount" &&  InputID != "DonationPreference") { //don't include the button
          userInput.push(InputValue);
          console.log(InputValue);
          if (InputValue == null || InputValue == ""){ //checks if something is written in box
            ElP.setAttribute('style', 'visibility:visible');
            validateArray.push(false);
          } else {
            ElP.setAttribute('style', 'visibility:hidden')
            checkEmail = true;
            validateArray.push(true);
          }
          if (checkEmail && InputID == 'DonationEmail') { //checks if something is a valid email
            var validEmail = validateEmail(InputValue);
              if (validEmail) { //checks if input is true or false (and email is valid)
                ElP.setAttribute('style', 'visibility:hidden');
                validateArray.push(true);
              } else {
                ElP.setAttribute('style', 'visibility:visible');
                validateArray.push(false);
              }
          }
      }
    }
    ClearFields(validateArray); //clearing fields after valid input
  }

//Local Storage

function storeFormValues (e) {
  console.log(e);
  var elForm = e.target.form;
  var localStorageArray = [];
  for (var i = 0; i < elForm.length; i++) {
    var el = elForm[i];
    var InputValue = el.value;
    var InputID = el.id;
    if (InputID != "SubmitButton" && InputID !=  "DonationAmount" &&  InputID != "DonationPreference"){
      var InputObject = {};
      InputObject.InputValue = InputValue;
      InputObject.InputID = InputID;
      localStorageArray.push(InputObject);
    }
  }
  saveStorage('DonationsForm', localStorageArray);
}

function populateForm (){ //puts saved data into form
  var DonationFormStorage = getStorage('DonationsForm');
  if (DonationFormStorage !== null) { //if there's something inside storage use it populate form
    for (var i = 0; i < DonationFormStorage.length; i++) {
      var inputId = DonationFormStorage[i].InputID;
      var inputValue = DonationFormStorage[i].InputValue;
      var elInput = document.getElementById(inputId);
      elInput.value = inputValue;
    }
  }
}

populateForm();
<commit_msg>made it so the local storage gets cleared on valid input<commit_after>// Function to validate the E-Mail input in the DonationForm

function validateEmail(email) {
    var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(email);
}

var userInput = [];
var DonationForm = document.getElementById("donationForm");

//Function to clear form after valid input

function ClearFields (validateArray){
  if (validateArray.indexOf(false) < 0){
    DonationForm.reset();
    localStorage.removeItem('DonationsForm');
  }
}

//Local Storage

function saveStorage(key, UserData) {
  var temp = JSON.stringify(UserData);
  localStorage.setItem(key, temp);
}

function getStorage(key){
  var temp = localStorage.getItem(key);
  return JSON.parse(temp);
}

//Event Listeners for Submit Button

DonationForm.addEventListener("submit", validateDonationForm);
DonationForm.addEventListener("keyup", storeFormValues);

// Function to validate the Name input in the DonationForm

function validateDonationForm(e) {
    e.preventDefault();
    var validateArray = [];
    for (var i = 0; i < e.target.length; i++) {
      var el = e.target[i];
      var InputValue = el.value;
      var InputID = el.id;
      var ElP = el.nextElementSibling;
      var checkEmail = false;
        if (InputID != "SubmitButton" && InputID !=  "DonationAmount" &&  InputID != "DonationPreference") { //don't include the button
          userInput.push(InputValue);
          console.log(InputValue);
          if (InputValue == null || InputValue == ""){ //checks if something is written in box
            ElP.setAttribute('style', 'visibility:visible');
            validateArray.push(false);
          } else {
            ElP.setAttribute('style', 'visibility:hidden')
            checkEmail = true;
            validateArray.push(true);
          }
          if (checkEmail && InputID == 'DonationEmail') { //checks if something is a valid email
            var validEmail = validateEmail(InputValue);
              if (validEmail) { //checks if input is true or false (and email is valid)
                ElP.setAttribute('style', 'visibility:hidden');
                validateArray.push(true);
              } else {
                ElP.setAttribute('style', 'visibility:visible');
                validateArray.push(false);
              }
          }
      }
    }
    ClearFields(validateArray); //clearing fields after valid input
  }

//Local Storage

function storeFormValues (e) {
  console.log(e);
  var elForm = e.target.form;
  var localStorageArray = [];
  for (var i = 0; i < elForm.length; i++) {
    var el = elForm[i];
    var InputValue = el.value;
    var InputID = el.id;
    if (InputID != "SubmitButton" && InputID !=  "DonationAmount" &&  InputID != "DonationPreference"){
      var InputObject = {};
      InputObject.InputValue = InputValue;
      InputObject.InputID = InputID;
      localStorageArray.push(InputObject);
    }
  }
  saveStorage('DonationsForm', localStorageArray);
}

function populateForm (){ //puts saved data into form
  var DonationFormStorage = getStorage('DonationsForm');
  if (DonationFormStorage !== null) { //if there's something inside storage use it populate form
    for (var i = 0; i < DonationFormStorage.length; i++) {
      var inputId = DonationFormStorage[i].InputID;
      var inputValue = DonationFormStorage[i].InputValue;
      var elInput = document.getElementById(inputId);
      elInput.value = inputValue;
    }
  }
}

populateForm();
<|endoftext|>


<commit_before>/**
 * Physics body component for a single Leap Motion hand.
 */
module.exports = {
  dependencies: ['leap-hand'],

  init: function () {
    this.system = this.el.sceneEl.systems.leap;
    this.physics = this.el.sceneEl.systems.physics;
    this.physics.addBehavior(this, this.physics.Phase.SIMULATE);

    this.palmBody = /** @type {CANNON.Body} */ null;
    this.fingerBodies = /** @type {{string: CANNON.Body}} */ {};
  },

  remove: function () {
    this.system.removeBehavior(this, this.physics.Phase.SIMULATE);
    for (var id in this.fingerBodies) {
      if (this.fingerBodies.hasOwnProperty(id)) {
        this.physics.removeBody(this.fingerBodies[id]);
      }
    }
  },

  step: function () {
    var finger, fingerBody,
        hand = this.el.components['leap-hand'].getHand();

    if (!hand || !hand.valid) return;

    this.syncPalmBody(hand, this.palmBody || this.createPalmBody());

    for (var i = 0; i < hand.fingers.length; i++) {
      finger = hand.fingers[i];
      if (finger.valid) {
        fingerBody = this.fingerBodies[finger.type] || this.createFingerBody(finger);
        this.syncFingerBody(finger, fingerBody);
      }
    }
  },

  createFingerBody: function (finger) {
    var body = new CANNON.Body({
      shape: new CANNON.Sphere(finger.distal.length / 2),
      material: this.physics.material,
      mass: 0,
      fixedRotation: true
    });
    body.el = this.el;
    this.physics.addBody(body);
    this.fingerBodies[finger.type] = body;
    return body;
  },

  syncFingerBody: (function () {
    var position = new THREE.Vector3();

    return function (finger, body) {
      this.el.object3D.localToWorld(position.fromArray(finger.distal.center()));
      body.position.copy(position);
      body.shapes[0].radius = finger.distal.length / 2;
    };
  }()),

  createPalmBody: function () {
    var body = new CANNON.Body({
      shape: new CANNON.Sphere(0.01),
      material: this.physics.material,
      mass: 0
    });
    body.el = this.el;
    this.physics.addBody(body);
    this.palmBody = body;
    return body;
  },

  syncPalmBody: (function () {
    var position = new THREE.Vector3(),
        normal = new THREE.Vector3(),
        baseNormal = new THREE.Vector3(0, -1, 0),
        rotation = new THREE.Quaternion();

    return function (hand, body) {
      this.el.object3D.localToWorld(normal.fromArray(hand.palmNormal));
      rotation.setFromUnitVectors(baseNormal, normal);
      body.quaternion.copy(rotation);

      this.el.object3D.localToWorld(position.fromArray(hand.palmPosition));
      body.position.copy(position);
    };
  }())
};
<commit_msg>Improved hand rotation -> physics tracking.<commit_after>/**
 * Physics body component for a single Leap Motion hand.
 */
module.exports = {
  dependencies: ['leap-hand'],

  init: function () {
    this.system = this.el.sceneEl.systems.leap;
    this.physics = this.el.sceneEl.systems.physics;
    this.physics.addBehavior(this, this.physics.Phase.SIMULATE);

    this.palmBody = /** @type {CANNON.Body} */ null;
    this.fingerBodies = /** @type {{string: CANNON.Body}} */ {};
  },

  remove: function () {
    this.system.removeBehavior(this, this.physics.Phase.SIMULATE);
    for (var id in this.fingerBodies) {
      if (this.fingerBodies.hasOwnProperty(id)) {
        this.physics.removeBody(this.fingerBodies[id]);
      }
    }
  },

  step: function () {
    var finger, fingerBody,
        hand = this.el.components['leap-hand'].getHand();

    if (!hand || !hand.valid) return;

    this.syncPalmBody(hand, this.palmBody || this.createPalmBody());

    for (var i = 0; i < hand.fingers.length; i++) {
      finger = hand.fingers[i];
      if (finger.valid) {
        fingerBody = this.fingerBodies[finger.type] || this.createFingerBody(finger);
        this.syncFingerBody(finger, fingerBody);
      }
    }
  },

  createFingerBody: function (finger) {
    var body = new CANNON.Body({
      shape: new CANNON.Sphere(finger.distal.length / 2),
      material: this.physics.material,
      mass: 0,
      fixedRotation: true
    });
    body.el = this.el;
    this.physics.addBody(body);
    this.fingerBodies[finger.type] = body;
    return body;
  },

  syncFingerBody: (function () {
    var position = new THREE.Vector3();

    return function (finger, body) {
      this.el.object3D.localToWorld(position.fromArray(finger.distal.center()));
      body.position.copy(position);
      body.shapes[0].radius = finger.distal.length / 2;
    };
  }()),

  createPalmBody: function () {
    var body = new CANNON.Body({
      shape: new CANNON.Sphere(0.01),
      material: this.physics.material,
      mass: 0
    });
    body.el = this.el;
    this.physics.addBody(body);
    this.palmBody = body;
    return body;
  },

  /**
   * Repositions and rotates the Body instance to match the Leap hand.
   * TODO: There are some residual rotation issues here.
   * @param {LEAP.Hand} hand
   * @param {CANNON.Body} body
   */
  syncPalmBody: (function () {
    var position = new THREE.Vector3(),
        rotation = new THREE.Quaternion(),
        hmdRotation = new THREE.Quaternion(),
        euler = new THREE.Euler(),
        _tmp1 = new THREE.Vector3(),
        _tmp2 = new THREE.Vector3();

    return function (hand, body) {
      rotation.setFromEuler(euler.set(hand.pitch(), hand.yaw(), hand.roll()));
      this.el.object3D.matrixWorld.decompose(_tmp1, hmdRotation, _tmp2);
      body.quaternion.copy(hmdRotation.multiply(rotation));

      this.el.object3D.localToWorld(position.fromArray(hand.palmPosition));
      body.position.copy(position);
    };
  }())
};
<|endoftext|>