<commit_before>(function () {
  "use strict";

  var app = angular.module('viewCustom', ['angularLoad']);

  //add "try this search elsewhere" options
  app.component('prmSearchResultListAfter', {
    controller: 'SearchElsewhereController',
    bindings: {
      'parentCtrl': '='
    },
    template: '
' +
                '
try your search in WorldCat
' +
                '
try your search in Google Scholar
' +
              '
'
  })
  .controller('SearchElsewhereController', ['$scope', function($scope){
    var vm = this,
        getTerms = function(){
          var terms = "",
              searchFields = vm.parentCtrl.searchService.searchFieldsService;
          if (searchFields.advancedSearch){
            terms = "";
            searchFields.searchParams.query.forEach(function(t){
              terms += t.replace(/^[^,]+,[^,]+,/, '').replace(/,[^,]+$/, '') + " ";
            });
            terms = terms.replace(/^\s+|\s+$/, "");
          }
          else{
            terms = searchFields.mainSearch;
          }
          return terms || "";
        };

    $scope.searchWorldcat = function(){
      window.open("https://www.worldcat.org/search?q=" + encodeURIComponent(getTerms()), "_blank");
    };
    $scope.searchGoogleScholar = function(){
      window.open("https://scholar.google.ca/scholar?q=" + encodeURIComponent(getTerms()), "_blank");
    };

    vm.$onInit = function(){
      console.log('SearchElsewhereController $onInit()');
    };
  }]);

  //add LiveHelp button/options
  app.component('prmExploreMainAfter', {
    controller: 'LiveHelpController',
    templateUrl: 'custom/ACAD/html/live-help.html'
  })
  .controller('LiveHelpController', ['$scope', 'angularLoad', function($scope, angularLoad){
    var ctrl       = this,
        chatWindow = null,
        chatUrl    = (function(){
          var url      = 'https://libraryh3lp.com/chat/novanet@chat.libraryh3lp.com',
              skin     = '10162',
              theme    = 'gota',
              title    = 'Live Help',
              identity = 'Librarian',
              css      = 'https://sites.stfx.ca/library/sites/sites.stfx.ca.library/files/libraryh3lp.css';

          url += '?skin='     + encodeURIComponent(skin);
          url += '&theme='    + encodeURIComponent(theme);
          url += '&title='    + encodeURIComponent(title);
          url += '&identity=' + encodeURIComponent(identity);
          url += '&css='      + encodeURIComponent(css);
          return url;
        })();

    ctrl.$onInit = function (){
      angularLoad.loadScript(
        'https://libraryh3lp.com/js/libraryh3lp.js?multi,poll'
      ).then(
        function(){
          //console.log('Loaded external libraryh3lp script.');
        },
        function(data){
          console.error('Failed to load external libraryh3lp script.');
          console.error(JSON.stringify(data, null, 2));
      });
    };

    $scope.liveHelpText = "Ask A Librarian!";

    $scope.showChat = function(evt){
      evt && evt.stopPropagation();

      if (chatWindow && !chatWindow.closed){
        chatWindow.focus();
      }
      else{
        chatWindow = window.open(chatUrl, 'Live Help', 'resizable=1,width=300,height=300,top:100,left:100');
      }
    };

  }]); //end definition of LiveHelpController

})();
<commit_msg>switch to dom manip instead of using ng-click attribute<commit_after>(function () {
  "use strict";

  var app = angular.module('viewCustom', ['angularLoad']);

  //add "try this search elsewhere" options
  app.component('prmSearchResultListAfter', {
    controller: 'SearchElsewhereController',
    bindings: {
      'parentCtrl': '='
    },
    template: '
' +
                '
try your search in WorldCat
' +
                '
try your search in Google Scholar
' +
              '
'
  })
  .controller('SearchElsewhereController', ['$scope', function($scope){
    var vm = this,
        getTerms = function(){
          var terms = "",
              searchFields = vm.parentCtrl.searchService.searchFieldsService;
          if (searchFields.advancedSearch){
            terms = "";
            searchFields.searchParams.query.forEach(function(t){
              terms += t.replace(/^[^,]+,[^,]+,/, '').replace(/,[^,]+$/, '') + " ";
            });
            terms = terms.replace(/^\s+|\s+$/, "");
          }
          else{
            terms = searchFields.mainSearch;
          }
          return terms || "";
        };

    vm.$onInit = function(){
      var terms = getTerms(),
          worldCatLink      = angular.element(document.getElementById("searchWorldCat")),
          googleScholarLink = angular.element(document.getElementById("searchGoogleScholar"));

      worldCatLink.attr("href", "https://www.worldcat.org/search?q=" + encodeURIComponent(terms));
      googleScholarLink.attr("href", "https://scholar.google.ca/scholar?q=" + encodeURIComponent(terms));
    };
  }]);

  //add LiveHelp button/options
  app.component('prmExploreMainAfter', {
    controller: 'LiveHelpController',
    templateUrl: 'custom/ACAD/html/live-help.html'
  })
  .controller('LiveHelpController', ['$scope', 'angularLoad', function($scope, angularLoad){
    var ctrl       = this,
        chatWindow = null,
        chatUrl    = (function(){
          var url      = 'https://libraryh3lp.com/chat/novanet@chat.libraryh3lp.com',
              skin     = '10162',
              theme    = 'gota',
              title    = 'Live Help',
              identity = 'Librarian',
              css      = 'https://sites.stfx.ca/library/sites/sites.stfx.ca.library/files/libraryh3lp.css';

          url += '?skin='     + encodeURIComponent(skin);
          url += '&theme='    + encodeURIComponent(theme);
          url += '&title='    + encodeURIComponent(title);
          url += '&identity=' + encodeURIComponent(identity);
          url += '&css='      + encodeURIComponent(css);
          return url;
        })();

    ctrl.$onInit = function (){
      angularLoad.loadScript(
        'https://libraryh3lp.com/js/libraryh3lp.js?multi,poll'
      ).then(
        function(){
          //console.log('Loaded external libraryh3lp script.');
        },
        function(data){
          console.error('Failed to load external libraryh3lp script.');
          console.error(JSON.stringify(data, null, 2));
      });
    };

    $scope.liveHelpText = "Ask A Librarian!";

    $scope.showChat = function(evt){
      evt && evt.stopPropagation();

      if (chatWindow && !chatWindow.closed){
        chatWindow.focus();
      }
      else{
        chatWindow = window.open(chatUrl, 'Live Help', 'resizable=1,width=300,height=300,top:100,left:100');
      }
    };

  }]); //end definition of LiveHelpController

})();
<|endoftext|>


<commit_before>var version = {
	updateInterval: 600000,
	intervalId: null
}

version.checkVersion = function () {

	$.ajax({
		type: 'GET',
		url: 'githash.php',
		success: function (data) {
			// The githash variable is located in index.php
			if (data && data.gitHash !== gitHash) {
				window.location.reload();
				window.location.href = window.location.href;
			}
		},
		error: function () {

		}
	});

}

version.init = function () {

	this.intervalId = setInterval(function () {
		this.checkVersion();
	}.bind(this), this.updateInterval);

}<commit_msg>add comments to version.js<commit_after>var version = {
	updateInterval: 600000,
	intervalId: null
}

/**
 * Checks the version and refreshes the page if a new version has been pulled
 */
version.checkVersion = function () {

	$.ajax({
		type: 'GET',
		url: 'githash.php',
		success: function (data) {
			// The githash variable is located in index.php
			if (data && data.gitHash !== gitHash) {
				window.location.reload();
				window.location.href = window.location.href;
			}
		},
		error: function () {

		}
	});

}

version.init = function () {

	this.intervalId = setInterval(function () {
		this.checkVersion();
	}.bind(this), this.updateInterval);

}<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import PropTypes from 'prop-types';
import Dropdown from './Dropdown';
import SelectTrigger from './SelectTrigger';
import DropdownMenu from './DropdownMenu';
import { matcher } from './utils';

const KEYCODES = {
  UP_ARROW: 38,
  DOWN_ARROW: 40,
  ENTER: 13,
  TAB: 9,
  BACK_SPACE: 8,
};

const actions = {
  38: 'handleUpArrow',
  40: 'handleDownArrow',
  13: 'handleEnterPress',
  9: 'handleTabPress',
};

const noop = () => {};

export default class Select extends Component {
  state = {
    highlightedIndex: null,
    isOpen: false,
    focused: false,
    filteredOptions: null,
    searchTerm: null,
  };

  documentEventListeners = {
    handleEscapePress: ::this.handleEscapePress,
    handleDocumentClick: ::this.handleDocumentClick,
  };

  componentDidMount() {
    document.addEventListener(
      'keydown',
      this.documentEventListeners.handleEscapePress
    );
    document.addEventListener(
      'click',
      this.documentEventListeners.handleDocumentClick,
      true
    );
  }

  componentWillUnmount() {
    document.removeEventListener(
      'keydown',
      this.documentEventListeners.handleEscapePress
    );
    document.removeEventListener(
      'click',
      this.documentEventListeners.handleDocumentClick,
      true
    );
  }

  highlightOption(highlightedIndex) {
    let options = this.state.filteredOptions || this.props.options;
    let highlightedOption = options[highlightedIndex];

    this.setState({
      highlightedIndex,
      highlightedOption,
    });
  }

  selectOption = (highlightedIndex, option) => {
    let options = this.state.filteredOptions || this.props.options;
    let selectedOption = option || options[highlightedIndex];
    this.highlightOption(highlightedIndex);
    if (selectedOption) {
      this.props.onChange({
        option: selectedOption,
        select: this.getPublicApi(),
      });
    }
    this.setState({
      searchTerm: null,
    });
  };

  open = () => {
    if (this.props.disabled) {
      return;
    }

    let highlightedIndex = this.state.highlightedIndex;
    let { options, selected } = this.props;
    highlightedIndex = highlightedIndex !== null
      ? highlightedIndex
      : options.indexOf(selected);

    this.highlightOption(highlightedIndex);
    this.setState({
      isOpen: true,
    });

    this.props.onOpen({ select: this.getPublicApi() });
  };

  close = () => {
    this.highlightOption(null);
    this.setState({
      isOpen: false,
      filteredOptions: null,
      searchTerm: null,
    });
    this.props.onClose({ select: this.getPublicApi() });
  };

  toggle = event => {
    event && event.stopPropagation();
    if (this.state.isOpen) {
      this.close();
    } else {
      this.open();
    }
  };

  setFocusedState(focused) {
    this.setState({ focused });
  }

  focusField = () => {
    setTimeout(() => {
      this.powerselect.focus();
    });
  };

  search = (searchTerm, callback) => {
    let {
      options,
      optionLabelPath,
      searchIndices = optionLabelPath,
    } = this.props;
    let filteredOptions = null;

    if (searchTerm) {
      filteredOptions = options.filter(option => {
        return this.props.matcher({
          option,
          searchTerm,
          searchIndices,
        });
      });
    }

    if (!searchTerm || !filteredOptions.length) {
      let highlightedIndex = -1;
      this.highlightOption(highlightedIndex);
    }

    if (searchTerm && filteredOptions.length) {
      // let firstOption = filteredOptions[0]
      // if (searchTerm.toLowerCase() === firstOption.toLowerCase() || (optionLabelPath && searchTerm.toLowerCase() === (firstOption[optionLabelPath] || '').toLowerCase())) {
      this.highlightOption(0);
      // }
    }

    this.setState(
      {
        filteredOptions,
        searchTerm,
      },
      callback
    );
  };

  handleSearchInputChange = event => {
    let value = event.target.value;
    this.search(value, this.open);
    this.props.onSearchInputChange(event, { select: this.getPublicApi() });
  };

  validateAndHighlightOption({
    options = [],
    event,
    highlightedIndex,
    recursiveFn,
  }) {
    let isValidOptionAvailable = !!options.find(option => !option.disabled);
    if (isValidOptionAvailable) {
      let highlightedOption = options[highlightedIndex];
      if (highlightedOption.disabled) {
        return recursiveFn.call(this, event, highlightedIndex);
      }
      this.highlightOption(highlightedIndex);
    }
  }

  handleDownArrow(event, index) {
    let options = this.state.filteredOptions || this.props.options;
    let highlightedIndex = index < options.length - 1 ? ++index : 0;

    this.validateAndHighlightOption({
      options,
      event,
      highlightedIndex,
      recursiveFn: this.handleDownArrow,
    });
  }

  handleUpArrow(event, index) {
    let options = this.state.filteredOptions || this.props.options;
    let highlightedIndex = index > 0 && index <= options.length
      ? --index
      : options.length - 1;

    this.validateAndHighlightOption({
      options,
      event,
      highlightedIndex,
      recursiveFn: this.handleUpArrow,
    });
  }

  handleEnterPress(event, highlightedIndex) {
    if (this.state.isOpen) {
      this.selectOption(highlightedIndex);
      this.focusField();
      this.close();
    }
    if (highlightedIndex === -1) {
      this.props.onEnter(event, { select: this.getPublicApi() });
    }
  }

  handleTabPress(event, highlightedIndex) {
    this.setFocusedState(false);
    if (this.state.isOpen) {
      this.selectOption(highlightedIndex);
      this.close();
    }
  }

  handleBackspacePress(event, highlightedIndex) {
    this.props.onBackspacePress(event, { select: this.getPublicApi() });
  }

  handleKeyDown = (...args) => {
    let [event] = args;
    let keyCode = event.which;
    let action = this[actions[keyCode]];
    if (action) {
      if (
        (keyCode === KEYCODES.UP_ARROW || keyCode === KEYCODES.DOWN_ARROW) &&
        !this.state.isOpen
      ) {
        this.open();
        return;
      }

      action.apply(this, args);
    }

    this.props.onKeyDown(event, { select: this.getPublicApi() });
  };

  handleEscapePress(event) {
    if (event.which === 27) {
      this.close();
    }
  }

  handleDocumentClick(event) {
    let $target = event.target;
    let powerselect = this.powerselect;

    if (
      !(powerselect.contains($target) || $target.closest('.PowerSelect__Menu'))
    ) {
      let { focused, isOpen } = this.state;
      if (focused) {
        this.setFocusedState(false);
      }

      if (isOpen) {
        // this.selectOption(this.state.highlightedIndex)
        this.close();
      }
    }
  }

  handleFocus = event => {
    this.setFocusedState(true);
    this.props.onFocus(event, { select: this.getPublicApi() });
  };

  handleBlur = event => {
    this.setFocusedState(false);
    this.props.onBlur(event, { select: this.getPublicApi() });
  };

  handleClick = event => {
    this.toggle(event);
    this.props.onClick(event, { select: this.getPublicApi() });
  };

  handleOptionClick = (highlightedIndex, option) => {
    this.selectOption(highlightedIndex, option);
    this.focusField();

    if (this.props.closeOnOptionClick) {
      this.close();
    }
  };

  getPublicApi() {
    let { isOpen, searchTerm } = this.state;

    return {
      open: this.open,
      close: this.close,
      toggle: this.toggle,
      search: this.search,
      focus: this.focusField,
      isOpen,
      searchTerm,
    };
  }

  render() {
    let {
      onRef,
      className,
      tabIndex,
      options,
      selected,
      optionLabelPath,
      optionComponent,
      placeholder,
      disabled,
      selectedOptionComponent,
      beforeOptionsComponent,
      afterOptionsComponent,
    } = this.props;

    let { isOpen, searchTerm } = this.state;
    let filteredOptions = this.state.filteredOptions || options;
    let SelectTrigger = this.props.selectTriggerComponent;
    let selectApi = this.getPublicApi();
    let { highlightedIndex, highlightedOption, focused } = this.state;

    return (
      <Dropdown>
        <div
          ref={powerselect => {
            this.powerselect = powerselect;
            if (onRef) {
              onRef(powerselect);
            }
          }}
          className={`PowerSelect ${className} ${disabled
            ? 'PowerSelect--disabled'
            : ''} ${isOpen ? 'PowerSelect--open' : ''} ${focused
            ? 'PowerSelect--focused'
            : ''} ${searchTerm ? 'PowerSelect__WithSearch' : ''}`}
          tabIndex={tabIndex}
          onFocus={() => {
            let triggerInput = this.powerselect.querySelector('input');
            if (triggerInput) {
              triggerInput.focus();
            }
          }}
          onKeyDown={event => {
            this.handleKeyDown(event, highlightedIndex);
          }}
        >
          <SelectTrigger
            selectedOption={selected}
            optionLabelPath={optionLabelPath}
            selectedOptionComponent={selectedOptionComponent}
            placeholder={placeholder}
            disabled={disabled}
            searchTerm={searchTerm}
            handleOnChange={this.handleSearchInputChange}
            onClick={this.handleClick}
            handleOnFocus={this.handleFocus}
            handleOnBlur={this.handleBlur}
            select={selectApi}
          />
        </div>
        {isOpen &&
          <DropdownMenu
            minWidth={this.powerselect.offsetWidth}
            options={filteredOptions}
            selected={selected}
            optionLabelPath={optionLabelPath}
            optionComponent={optionComponent}
            onOptionClick={this.handleOptionClick}
            handleKeyDown={this.handleKeyDown}
            highlightedIndex={highlightedIndex}
            highlightedOption={highlightedOption}
            select={selectApi}
            beforeOptionsComponent={beforeOptionsComponent}
            afterOptionsComponent={afterOptionsComponent}
          />}
      </Dropdown>
    );
  }
}

Select.propTypes = {
  options: PropTypes.array.isRequired,
  selected: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object,
    PropTypes.array,
  ]),
  onChange: PropTypes.func.isRequired,
};

Select.displayName = 'Select';
Select.defaultProps = {
  options: [],
  disabled: false,
  tabIndex: 0,
  selectTriggerComponent: SelectTrigger,
  optionLabelPath: null,
  optionComponent: null,
  selectedOptionComponent: null,
  beforeOptionsComponent: null,
  afterOptionsComponent: null,
  matcher: matcher,
  onFocus: noop,
  onBlur: noop,
  onClick: noop,
  onKeyDown: noop,
  onEnter: noop,
  onOpen: noop,
  onClose: noop,

  closeOnOptionClick: true,
  onSearchInputChange: noop,
};
<commit_msg>Use KEY_CODES constant everywhere<commit_after>import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import PropTypes from 'prop-types';
import Dropdown from './Dropdown';
import SelectTrigger from './SelectTrigger';
import DropdownMenu from './DropdownMenu';
import { matcher } from './utils';

const KEY_CODES = {
  UP_ARROW: 38,
  DOWN_ARROW: 40,
  ENTER: 13,
  TAB: 9,
};

const actions = {
  [KEY_CODES.UP_ARROW]: 'handleUpArrow',
  [KEY_CODES.DOWN_ARROW]: 'handleDownArrow',
  [KEY_CODES.ENTER]: 'handleEnterPress',
  [KEY_CODES.TAB]: 'handleTabPress',
};

const noop = () => {};

export default class Select extends Component {
  state = {
    highlightedIndex: null,
    isOpen: false,
    focused: false,
    filteredOptions: null,
    searchTerm: null,
  };

  documentEventListeners = {
    handleEscapePress: ::this.handleEscapePress,
    handleDocumentClick: ::this.handleDocumentClick,
  };

  componentDidMount() {
    document.addEventListener(
      'keydown',
      this.documentEventListeners.handleEscapePress
    );
    document.addEventListener(
      'click',
      this.documentEventListeners.handleDocumentClick,
      true
    );
  }

  componentWillUnmount() {
    document.removeEventListener(
      'keydown',
      this.documentEventListeners.handleEscapePress
    );
    document.removeEventListener(
      'click',
      this.documentEventListeners.handleDocumentClick,
      true
    );
  }

  highlightOption(highlightedIndex) {
    let options = this.state.filteredOptions || this.props.options;
    let highlightedOption = options[highlightedIndex];

    this.setState({
      highlightedIndex,
      highlightedOption,
    });
  }

  selectOption = (highlightedIndex, option) => {
    let options = this.state.filteredOptions || this.props.options;
    let selectedOption = option || options[highlightedIndex];
    this.highlightOption(highlightedIndex);
    if (selectedOption) {
      this.props.onChange({
        option: selectedOption,
        select: this.getPublicApi(),
      });
    }
    this.setState({
      searchTerm: null,
    });
  };

  open = () => {
    if (this.props.disabled) {
      return;
    }

    let highlightedIndex = this.state.highlightedIndex;
    let { options, selected } = this.props;
    highlightedIndex = highlightedIndex !== null
      ? highlightedIndex
      : options.indexOf(selected);

    this.highlightOption(highlightedIndex);
    this.setState({
      isOpen: true,
    });

    this.props.onOpen({ select: this.getPublicApi() });
  };

  close = () => {
    this.highlightOption(null);
    this.setState({
      isOpen: false,
      filteredOptions: null,
      searchTerm: null,
    });
    this.props.onClose({ select: this.getPublicApi() });
  };

  toggle = event => {
    event && event.stopPropagation();
    if (this.state.isOpen) {
      this.close();
    } else {
      this.open();
    }
  };

  setFocusedState(focused) {
    this.setState({ focused });
  }

  focusField = () => {
    setTimeout(() => {
      this.powerselect.focus();
    });
  };

  search = (searchTerm, callback) => {
    let {
      options,
      optionLabelPath,
      searchIndices = optionLabelPath,
    } = this.props;
    let filteredOptions = null;

    if (searchTerm) {
      filteredOptions = options.filter(option => {
        return this.props.matcher({
          option,
          searchTerm,
          searchIndices,
        });
      });
    }

    if (!searchTerm || !filteredOptions.length) {
      let highlightedIndex = -1;
      this.highlightOption(highlightedIndex);
    }

    if (searchTerm && filteredOptions.length) {
      // let firstOption = filteredOptions[0]
      // if (searchTerm.toLowerCase() === firstOption.toLowerCase() || (optionLabelPath && searchTerm.toLowerCase() === (firstOption[optionLabelPath] || '').toLowerCase())) {
      this.highlightOption(0);
      // }
    }

    this.setState(
      {
        filteredOptions,
        searchTerm,
      },
      callback
    );
  };

  handleSearchInputChange = event => {
    let value = event.target.value;
    this.search(value, this.open);
    this.props.onSearchInputChange(event, { select: this.getPublicApi() });
  };

  validateAndHighlightOption({
    options = [],
    event,
    highlightedIndex,
    recursiveFn,
  }) {
    let isValidOptionAvailable = !!options.find(option => !option.disabled);
    if (isValidOptionAvailable) {
      let highlightedOption = options[highlightedIndex];
      if (highlightedOption.disabled) {
        return recursiveFn.call(this, event, highlightedIndex);
      }
      this.highlightOption(highlightedIndex);
    }
  }

  handleDownArrow(event, index) {
    let options = this.state.filteredOptions || this.props.options;
    let highlightedIndex = index < options.length - 1 ? ++index : 0;

    this.validateAndHighlightOption({
      options,
      event,
      highlightedIndex,
      recursiveFn: this.handleDownArrow,
    });
  }

  handleUpArrow(event, index) {
    let options = this.state.filteredOptions || this.props.options;
    let highlightedIndex = index > 0 && index <= options.length
      ? --index
      : options.length - 1;

    this.validateAndHighlightOption({
      options,
      event,
      highlightedIndex,
      recursiveFn: this.handleUpArrow,
    });
  }

  handleEnterPress(event, highlightedIndex) {
    if (this.state.isOpen) {
      this.selectOption(highlightedIndex);
      this.focusField();
      this.close();
    }
    if (highlightedIndex === -1) {
      this.props.onEnter(event, { select: this.getPublicApi() });
    }
  }

  handleTabPress(event, highlightedIndex) {
    this.setFocusedState(false);
    if (this.state.isOpen) {
      this.selectOption(highlightedIndex);
      this.close();
    }
  }

  handleKeyDown = (...args) => {
    let [event] = args;
    let keyCode = event.which;
    let action = this[actions[keyCode]];
    if (action) {
      if (
        (keyCode === KEY_CODES.UP_ARROW || keyCode === KEY_CODES.DOWN_ARROW) &&
        !this.state.isOpen
      ) {
        this.open();
        return;
      }

      action.apply(this, args);
    }
    this.props.onKeyDown(event, { select: this.getPublicApi() });
  };

  handleEscapePress(event) {
    if (event.which === 27) {
      this.close();
    }
  }

  handleDocumentClick(event) {
    let $target = event.target;
    let powerselect = this.powerselect;

    if (
      !(powerselect.contains($target) || $target.closest('.PowerSelect__Menu'))
    ) {
      let { focused, isOpen } = this.state;
      if (focused) {
        this.setFocusedState(false);
      }

      if (isOpen) {
        // this.selectOption(this.state.highlightedIndex)
        this.close();
      }
    }
  }

  handleFocus = event => {
    this.setFocusedState(true);
    this.props.onFocus(event, { select: this.getPublicApi() });
  };

  handleBlur = event => {
    this.setFocusedState(false);
    this.props.onBlur(event, { select: this.getPublicApi() });
  };

  handleClick = event => {
    this.toggle(event);
    this.props.onClick(event, { select: this.getPublicApi() });
  };

  handleOptionClick = (highlightedIndex, option) => {
    this.selectOption(highlightedIndex, option);
    this.focusField();

    if (this.props.closeOnOptionClick) {
      this.close();
    }
  };

  getPublicApi() {
    let { isOpen, searchTerm } = this.state;

    return {
      open: this.open,
      close: this.close,
      toggle: this.toggle,
      search: this.search,
      focus: this.focusField,
      isOpen,
      searchTerm,
    };
  }

  render() {
    let {
      onRef,
      className,
      tabIndex,
      options,
      selected,
      optionLabelPath,
      optionComponent,
      placeholder,
      disabled,
      selectedOptionComponent,
      beforeOptionsComponent,
      afterOptionsComponent,
    } = this.props;

    let { isOpen, searchTerm } = this.state;
    let filteredOptions = this.state.filteredOptions || options;
    let SelectTrigger = this.props.selectTriggerComponent;
    let selectApi = this.getPublicApi();
    let { highlightedIndex, highlightedOption, focused } = this.state;

    return (
      <Dropdown>
        <div
          ref={powerselect => {
            this.powerselect = powerselect;
            if (onRef) {
              onRef(powerselect);
            }
          }}
          className={`PowerSelect ${className} ${disabled
            ? 'PowerSelect--disabled'
            : ''} ${isOpen ? 'PowerSelect--open' : ''} ${focused
            ? 'PowerSelect--focused'
            : ''} ${searchTerm ? 'PowerSelect__WithSearch' : ''}`}
          tabIndex={tabIndex}
          onFocus={() => {
            let triggerInput = this.powerselect.querySelector('input');
            if (triggerInput) {
              triggerInput.focus();
            }
          }}
          onKeyDown={event => {
            this.handleKeyDown(event, highlightedIndex);
          }}
        >
          <SelectTrigger
            selectedOption={selected}
            optionLabelPath={optionLabelPath}
            selectedOptionComponent={selectedOptionComponent}
            placeholder={placeholder}
            disabled={disabled}
            searchTerm={searchTerm}
            handleOnChange={this.handleSearchInputChange}
            onClick={this.handleClick}
            handleOnFocus={this.handleFocus}
            handleOnBlur={this.handleBlur}
            select={selectApi}
          />
        </div>
        {isOpen &&
          <DropdownMenu
            minWidth={this.powerselect.offsetWidth}
            options={filteredOptions}
            selected={selected}
            optionLabelPath={optionLabelPath}
            optionComponent={optionComponent}
            onOptionClick={this.handleOptionClick}
            handleKeyDown={this.handleKeyDown}
            highlightedIndex={highlightedIndex}
            highlightedOption={highlightedOption}
            select={selectApi}
            beforeOptionsComponent={beforeOptionsComponent}
            afterOptionsComponent={afterOptionsComponent}
          />}
      </Dropdown>
    );
  }
}

Select.propTypes = {
  options: PropTypes.array.isRequired,
  selected: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object,
    PropTypes.array,
  ]),
  onChange: PropTypes.func.isRequired,
};

Select.displayName = 'Select';
Select.defaultProps = {
  options: [],
  disabled: false,
  tabIndex: 0,
  selectTriggerComponent: SelectTrigger,
  optionLabelPath: null,
  optionComponent: null,
  selectedOptionComponent: null,
  beforeOptionsComponent: null,
  afterOptionsComponent: null,
  matcher: matcher,
  onFocus: noop,
  onBlur: noop,
  onClick: noop,
  onKeyDown: noop,
  onEnter: noop,
  onOpen: noop,
  onClose: noop,

  closeOnOptionClick: true,
  onSearchInputChange: noop,
};
<|endoftext|>


<commit_before>var __ = require('underscore'),
    Backbone = require('backbone'),
    $ = require('jquery'),
    loadTemplate = require('../utils/loadTemplate'),
    Polyglot = require('node-polyglot'),
    languagesModel = require('../models/languagesMd'),
    countryListView = require('../views/countryListVw'),
    currencyListView = require('../views/currencyListVw'),
    languageListView = require('../views/languageListVw'),
    adminPanelView = require('../views/adminPanelVw'),
    notificationsPanelView = require('../views/notificationsPanelVw'),
    remote = require('remote');

module.exports = Backbone.View.extend({

  el: '#pageNav',

  events: {
    'click .js-navClose': 'navCloseClick',
    'click .js-navMin': 'navMinClick',
    'click .js-navMax': 'navMaxClick',
    'click .js-navBack': 'navBackClick',
    'click .js-navFwd': 'navFwdClick',
    'click .js-navNotifications': 'navNotificationsClick',
    'click .js-navProfile': 'navProfileClick',
    'click .js-navRefresh': 'navRefreshClick',
    'click .js-navAdminPanel': 'navAdminPanel',
    'click .js-navProfileMenu a': 'closeNav',
    'click .js-homeModal-countrySelect': 'countrySelect',
    'click .js-homeModal-currencySelect': 'currencySelect',
    'click .js-homeModal-languageSelect': 'languageSelect',
    'click .js-homeModal-timeSelect': 'timeSelect',
    'click .js-homeModal-newHandle': 'newHandle',
    'click .js-homeModal-existingHandle': 'existingHandle',
    'click .js-homeModal-cancelHandle': 'cancelHandle',
    'change .js-homeModalAvatarUpload': 'uploadAvatar',
    'click .js-homeModalDone': 'settingsDone',
    'click .js-closeModal': 'closeModal',
    'keyup .js-navAddressBar': 'addressBarKeyup',
    'click .js-closeStatus': 'closeStatusBar',
    'click .js-homeModal-themeSelected': 'setSelectedTheme'
  },

  initialize: function(options){
    "use strict";
    var self = this;
    this.socketView = options.socketView;
    this.subViews = [];
    this.languages = new languagesModel();
    this.options = options || {};

    this.currentWindow = remote.getCurrentWindow();

    //when language is changed, re-render
    this.listenTo(this.model, 'change:language', function(){
      var newLang = this.model.get("language");
      window.polyglot = new Polyglot({locale: newLang});
      window.polyglot.extend(__.where(this.languages.get('languages'), {langCode: newLang})[0]);
      this.render();
      //refresh the current page
      Backbone.history.loadUrl();
    });

    this.listenTo(window.obEventBus, "socketMessageRecived", function(response){
      this.handleSocketMessage(response);
    });
    this.socketNotificationID = Math.random().toString(36).slice(2);
    this.socketView.getNotifications(this.socketNotificationID);

    this.render();
  },

  handleSocketMessage: function(response) {
    "use strict";
    var data = JSON.parse(response.data);
    if(data.id == this.socketNotificationID){
      console.log(data);
    }
  },

  initAccordion: function(targ){
    "use strict";
    var acc = $(targ);
    var accWidth = acc.width();
    var accHeight = acc.height();
    var accChildren = acc.find('.accordion-child');
    var accNum = accChildren.length;
    var accWin = acc.find('.accordion-window');

    accWin.css({'left':0, 'width': function(){return accWidth * accNum;}});
    accChildren.css({'width':accWidth, 'height':accHeight});
    acc.find('.js-accordionNext').on('click', function(){
      var oldPos = accWin.css('left').replace("px","");
      if(oldPos > (accWidth * accNum * -1 + accWidth)){
        accWin.css('left', function(){
          return parseInt(accWin.css('left').replace("px","")) - accWidth;
        });
      }
      // focus search input
      $(this).closest('.accordion-child').next('.accordion-child').find('input:visible:first').focus();
    });
    acc.find('.js-accordionPrev').on('click', function(){
      var oldPos = accWin.css('left').replace("px","");
      if(oldPos < (0)){
        accWin.css('left', function(){
          return parseInt(accWin.css('left').replace("px","")) + accWidth;
        });
      }
      // focus search input
       $(this).closest('.accordion-child').prev('.accordion-child').find('input:visible:first').focus();
    });
    //set up filterable lists.
    //TODO: this is terrible, change to run when an event is emmitted from the subviews marking them as done rendering
    setTimeout(function(){
      var List = window.List;
      var countryList = new List('homeModal-countryList', {valueNames: ['homeModal-country']});
      var currencyList = new List('homeModal-currencyList', {valueNames: ['homeModal-currency']});
      var timeList = new List('homeModal-timeList', {valueNames: ['homeModal-time']});
      var languageList = new List('homeModal-languageList', {valueNames: ['homeModal-language']});
    }, 1000);
  },

  closeNav: function(){
    "use strict";
    var targ = this.$el.find('.js-navProfileMenu');
    targ.addClass('hide');
    $('#overlay').addClass('fadeOut hide');
  },

  render: function(){
    "use strict";
    var self = this;
    loadTemplate('./js/templates/pageNav.html', function(loadedTemplate) {
      self.$el.html(loadedTemplate(self.model.toJSON()));
      self.countryList = new countryListView({el: '.js-homeModal-countryList', selected: self.model.get('country')});
      self.currencyList = new currencyListView({el: '.js-homeModal-currencyList', selected: self.model.get('currency_code')});
      self.languageList = new languageListView({el: '.js-homeModal-languageList', selected: self.model.get('language')});
      self.subViews.push(self.countryList);
      self.subViews.push(self.currencyList);
      self.subViews.push(self.languageList);
      self.initAccordion('.js-profileAccordion');
      if(self.model.get('beenSet')){
        self.$el.find('.js-homeModal').hide();
      }
      self.notificationsPanel = new notificationsPanelView({
        parentEl: '#notificationsPanel',
        socketView: self.socketView,
        serverUrl: self.options.model.attributes.serverUrl
      });
      self.listenTo(self.notificationsPanel, 'notificationsCounted', self.setNotificationCount);
      self.subViews.push(self.notificationsPanel);
      //add the admin panel
      self.adminPanel = new adminPanelView({model: self.model});
      self.subViews.push(self.adminPanel);
      self.addressInput = self.$el.find('.js-navAddressBar');
      self.addressBarGoBtn = self.$el.find('.js-navAddressBarGo');
      self.statusBar = self.$el.find('.js-navStatusBar');
      //listen for address bar set events
      self.listenTo(window.obEventBus, "setAddressBar", function(setText){
        self.addressInput.val(setText);
        self.closeStatusBar();
      });
    });
    return this;
  },

  navNotificationsClick: function(e){
    "use strict";
    e.stopPropagation();
    this.setNotificationCount("");
    var targ = this.$el.find('.js-navNotificationsMenu');
    targ.siblings('.popMenu').addClass('hide');
    if(targ.hasClass('hide')){
      targ.removeClass('hide');
      $('#overlay').removeClass('hide');
      $('html').on('click.closeNav', function(e){
        if($(e.target).closest(targ).length === 0){
          targ.addClass('hide');
          $('#overlay').addClass('hide');
          $(this).off('.closeNav');
        }
      });
    }else{
      targ.addClass('hide');
      $('#overlay').addClass('fadeOut hide');
    }
  },

  setNotificationCount: function(count){
    "use strict";
    console.log("setnotificationscount " + count);
    this.$el.find('.js-navNotifications').attr('data-count', count);
  },

  navProfileClick: function(e){
    "use strict";
    e.stopPropagation();
    var targ = this.$el.find('.js-navProfileMenu');
    targ.siblings('.popMenu').addClass('hide');
    if(targ.hasClass('hide')){
      targ.removeClass('hide');
      $('#overlay').removeClass('fadeOut hide');
      $('html').on('click.closeNav', function(e){
        if($(e.target).closest(targ).length === 0){
          targ.addClass('hide');
          $('#overlay').addClass('fadeOut hide');
          $(this).off('.closeNav');
        }
      });
    }else{
      targ.addClass('hide');
      $('#overlay').addClass('fadeOut hide');
    }
  },

  navCloseClick: function(){
    "use strict";
    var process = remote.process;
    if (process.platform != 'darwin') {
      this.currentWindow.close();
    } else {
      this.currentWindow.hide();
    }
  },

  navMinClick: function(){
    "use strict";
    this.currentWindow.minimize();
  },

  navMaxClick: function(){
    "use strict";
    if(this.currentWindow.isMaximized()){
      this.currentWindow.unmaximize();
    } else {
      this.currentWindow.maximize();
    }
  },

  navBackClick: function(){
    "use strict";
    window.history.back();
  },

  navFwdClick: function(){
    "use strict";
    window.history.forward();
  },

  navRefreshClick: function(){
    "use strict";
    this.currentWindow.reload();
  },

  addressBarKeyup: function(e){
    "use strict";
    var barText = this.addressInput.val();
    if(barText.length > 0){
      //detect enter key
      if (e.keyCode == 13){
        this.addressBarProcess(barText);
        this.addressBarGoBtn.addClass("fadeOut");
      } else {
        this.addressBarGoBtn.removeClass("fadeOut");
        this.closeStatusBar();
      }
    } else {
      this.addressBarGoBtn.addClass("fadeOut");
      this.closeStatusBar();
    }
  },

  addressBarProcess: function(addressBarText){
    "use strict";
    var guid = "",
        handle = "",
        state = "",
        itemHash = "",
        addressTextArray = addressBarText.split("/");

    state = addressTextArray[1] ? "/" + addressTextArray[1] : "";
    itemHash = addressTextArray[2] ? "/" + addressTextArray[2] : "";

    if(addressTextArray[0].charAt(0) == "@"){
      handle = addressTextArray[0];
      this.showStatusBar('Navigation by handle is not supported yet.');
    } else if(addressTextArray[0].length === 40){
      guid = addressTextArray[0];
      Backbone.history.navigate('#userPage/' + guid + state + itemHash, {trigger:true});
    } else {
      this.showStatusBar('This is not a valid user GUID or handle.');
    }

  },

  showStatusBar: function(msgText){
    "use strict";
    this.statusBar.find('.js-statusBarMessage').text(msgText);
    this.statusBar.removeClass('fadeOut');
  },

  closeStatusBar: function(){
    "use strict";
    this.statusBar.addClass('fadeOut');
  },

  countrySelect: function(e){
    "use strict";
    var targ = $(e.currentTarget);
    var ctry = targ.attr('data-code');
    $('.js-homeModal-countryList').find('input[type=radio]').prop("checked", false);
    targ.find('input[type=radio]').prop("checked", true);
    this.model.set('country', ctry);
  },

  currencySelect: function(e){
    "use strict";
    var targ = $(e.currentTarget); //TODO: Rename variables to be more readable
    //var crcy = targ.attr('data-name');
    var ccode = targ.attr('data-code');
    $('.js-homeModal-currencyList').find('input[type=radio]').prop("checked", false);
    targ.find('input[type=radio]').prop("checked", true);
    //this.model.set('currency', crcy);
    this.model.set('currency_code', ccode);
  },

  languageSelect: function(e){
    "use strict";
    var targ = $(e.currentTarget); //TODO: Rename variables to be more readable
    var lang = targ.attr('data-code');
    $('.js-homeModal-languageList').find('input[type=radio]').prop("checked", false);
    targ.find('input[type=radio]').prop("checked", true);
    this.model.set('language', lang);
  },

  timeSelect: function(e){
    "use strict";
    var inpt = $(e.target).closest('input[type=radio]'); //TODO: Rename variables to be more readable
    var tz = inpt.attr('id');
    $('.js-homeModal-timezoneList').find('input[type=radio]').prop("checked", false);
    inpt.prop("checked", true);
    this.model.set('time_zone', tz);
  },

  newHandle: function(e){
    "use strict";
    this.$el.find('.js-homeModal-handleInput').closest('.flexRow').removeClass('hide');
    this.$el.find('.js-homeModal-existingHandle').parent().addClass('hide');
    this.$el.find('.js-homeModal-cancelHandle').parent().removeClass('hide');
  },

  existingHandle: function(e){
    "use strict";
    //TODO: add code to connect handle here
  },

  cancelHandle: function(e){
    "use strict";
    this.$el.find('.js-homeModal-handleInput').closest('.flexRow').addClass('hide');
    this.$el.find('.js-homeModal-existingHandle').parent().removeClass('hide');
    this.$el.find('.js-homeModal-cancelHandle').parent().addClass('hide');
  },

  uploadAvatar: function(e){
    "use strict";
    var self = this;
    var reader = new FileReader();
    reader.onload = function (e) {
      self.$el.find('.js-avatarPreview').css('background', 'url(' + e.target.result + ') 50% 50% / cover no-repeat');
      //self.model.set('tempAvatar', e.target.result);
      //TODO: add canvas resizing here
    };
    reader.readAsDataURL($(e.target)[0].files[0]);
  },

  settingsDone: function(e){
    "use strict";
    this.model.set('beenSet',true);
    this.$el.find('.js-homeModal').hide();

    // Start application walkthrough (coming soon once I have better designs)
  },

  closeModal: function(e){
    "use strict";
    $(e.target).closest('.modal').addClass('fadeOut');
    console.log('sdfdsf');
  },

  navAdminPanel: function(){
    "use strict";
    this.$el.find('.js-adminModal').removeClass('fadeOut');
    this.adminPanel.updatePage();
  },

  close: function(){
    "use strict";
    __.each(this.subViews, function(subView) {
      if(subView.close){
        subView.close();
      }else{
        subView.remove();
      }
    });
    this.remove();
  },

  setSelectedTheme: function(e){
    "use strict"
    // Needs to save to the object and update the dom
  },

  shadeColor2: function shadeColor2(color, percent) {
    var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
    return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
  }

});

<commit_msg>Check for more than 99 notifications<commit_after>var __ = require('underscore'),
    Backbone = require('backbone'),
    $ = require('jquery'),
    loadTemplate = require('../utils/loadTemplate'),
    Polyglot = require('node-polyglot'),
    languagesModel = require('../models/languagesMd'),
    countryListView = require('../views/countryListVw'),
    currencyListView = require('../views/currencyListVw'),
    languageListView = require('../views/languageListVw'),
    adminPanelView = require('../views/adminPanelVw'),
    notificationsPanelView = require('../views/notificationsPanelVw'),
    remote = require('remote');

module.exports = Backbone.View.extend({

  el: '#pageNav',

  events: {
    'click .js-navClose': 'navCloseClick',
    'click .js-navMin': 'navMinClick',
    'click .js-navMax': 'navMaxClick',
    'click .js-navBack': 'navBackClick',
    'click .js-navFwd': 'navFwdClick',
    'click .js-navNotifications': 'navNotificationsClick',
    'click .js-navProfile': 'navProfileClick',
    'click .js-navRefresh': 'navRefreshClick',
    'click .js-navAdminPanel': 'navAdminPanel',
    'click .js-navProfileMenu a': 'closeNav',
    'click .js-homeModal-countrySelect': 'countrySelect',
    'click .js-homeModal-currencySelect': 'currencySelect',
    'click .js-homeModal-languageSelect': 'languageSelect',
    'click .js-homeModal-timeSelect': 'timeSelect',
    'click .js-homeModal-newHandle': 'newHandle',
    'click .js-homeModal-existingHandle': 'existingHandle',
    'click .js-homeModal-cancelHandle': 'cancelHandle',
    'change .js-homeModalAvatarUpload': 'uploadAvatar',
    'click .js-homeModalDone': 'settingsDone',
    'click .js-closeModal': 'closeModal',
    'keyup .js-navAddressBar': 'addressBarKeyup',
    'click .js-closeStatus': 'closeStatusBar',
    'click .js-homeModal-themeSelected': 'setSelectedTheme'
  },

  initialize: function(options){
    "use strict";
    var self = this;
    this.socketView = options.socketView;
    this.subViews = [];
    this.languages = new languagesModel();
    this.options = options || {};

    this.currentWindow = remote.getCurrentWindow();

    //when language is changed, re-render
    this.listenTo(this.model, 'change:language', function(){
      var newLang = this.model.get("language");
      window.polyglot = new Polyglot({locale: newLang});
      window.polyglot.extend(__.where(this.languages.get('languages'), {langCode: newLang})[0]);
      this.render();
      //refresh the current page
      Backbone.history.loadUrl();
    });

    this.listenTo(window.obEventBus, "socketMessageRecived", function(response){
      this.handleSocketMessage(response);
    });
    this.socketNotificationID = Math.random().toString(36).slice(2);
    this.socketView.getNotifications(this.socketNotificationID);

    this.render();
  },

  handleSocketMessage: function(response) {
    "use strict";
    var data = JSON.parse(response.data);
    if(data.id == this.socketNotificationID){
      console.log(data);
    }
  },

  initAccordion: function(targ){
    "use strict";
    var acc = $(targ);
    var accWidth = acc.width();
    var accHeight = acc.height();
    var accChildren = acc.find('.accordion-child');
    var accNum = accChildren.length;
    var accWin = acc.find('.accordion-window');

    accWin.css({'left':0, 'width': function(){return accWidth * accNum;}});
    accChildren.css({'width':accWidth, 'height':accHeight});
    acc.find('.js-accordionNext').on('click', function(){
      var oldPos = accWin.css('left').replace("px","");
      if(oldPos > (accWidth * accNum * -1 + accWidth)){
        accWin.css('left', function(){
          return parseInt(accWin.css('left').replace("px","")) - accWidth;
        });
      }
      // focus search input
      $(this).closest('.accordion-child').next('.accordion-child').find('input:visible:first').focus();
    });
    acc.find('.js-accordionPrev').on('click', function(){
      var oldPos = accWin.css('left').replace("px","");
      if(oldPos < (0)){
        accWin.css('left', function(){
          return parseInt(accWin.css('left').replace("px","")) + accWidth;
        });
      }
      // focus search input
       $(this).closest('.accordion-child').prev('.accordion-child').find('input:visible:first').focus();
    });
    //set up filterable lists.
    //TODO: this is terrible, change to run when an event is emmitted from the subviews marking them as done rendering
    setTimeout(function(){
      var List = window.List;
      var countryList = new List('homeModal-countryList', {valueNames: ['homeModal-country']});
      var currencyList = new List('homeModal-currencyList', {valueNames: ['homeModal-currency']});
      var timeList = new List('homeModal-timeList', {valueNames: ['homeModal-time']});
      var languageList = new List('homeModal-languageList', {valueNames: ['homeModal-language']});
    }, 1000);
  },

  closeNav: function(){
    "use strict";
    var targ = this.$el.find('.js-navProfileMenu');
    targ.addClass('hide');
    $('#overlay').addClass('fadeOut hide');
  },

  render: function(){
    "use strict";
    var self = this;
    loadTemplate('./js/templates/pageNav.html', function(loadedTemplate) {
      self.$el.html(loadedTemplate(self.model.toJSON()));
      self.countryList = new countryListView({el: '.js-homeModal-countryList', selected: self.model.get('country')});
      self.currencyList = new currencyListView({el: '.js-homeModal-currencyList', selected: self.model.get('currency_code')});
      self.languageList = new languageListView({el: '.js-homeModal-languageList', selected: self.model.get('language')});
      self.subViews.push(self.countryList);
      self.subViews.push(self.currencyList);
      self.subViews.push(self.languageList);
      self.initAccordion('.js-profileAccordion');
      if(self.model.get('beenSet')){
        self.$el.find('.js-homeModal').hide();
      }
      self.notificationsPanel = new notificationsPanelView({
        parentEl: '#notificationsPanel',
        socketView: self.socketView,
        serverUrl: self.options.model.attributes.serverUrl
      });
      self.listenTo(self.notificationsPanel, 'notificationsCounted', self.setNotificationCount);
      self.subViews.push(self.notificationsPanel);
      //add the admin panel
      self.adminPanel = new adminPanelView({model: self.model});
      self.subViews.push(self.adminPanel);
      self.addressInput = self.$el.find('.js-navAddressBar');
      self.addressBarGoBtn = self.$el.find('.js-navAddressBarGo');
      self.statusBar = self.$el.find('.js-navStatusBar');
      //listen for address bar set events
      self.listenTo(window.obEventBus, "setAddressBar", function(setText){
        self.addressInput.val(setText);
        self.closeStatusBar();
      });
    });
    return this;
  },

  navNotificationsClick: function(e){
    "use strict";
    e.stopPropagation();
    this.setNotificationCount("");
    var targ = this.$el.find('.js-navNotificationsMenu');
    targ.siblings('.popMenu').addClass('hide');
    if(targ.hasClass('hide')){
      targ.removeClass('hide');
      $('#overlay').removeClass('hide');
      $('html').on('click.closeNav', function(e){
        if($(e.target).closest(targ).length === 0){
          targ.addClass('hide');
          $('#overlay').addClass('hide');
          $(this).off('.closeNav');
        }
      });
    }else{
      targ.addClass('hide');
      $('#overlay').addClass('fadeOut hide');
    }
  },

  setNotificationCount: function(count){
    "use strict";
    console.log("setnotificationscount " + count);
    if(count > 99) {
      count = "..";
    }
    this.$el.find('.js-navNotifications').attr('data-count', count);
  },

  navProfileClick: function(e){
    "use strict";
    e.stopPropagation();
    var targ = this.$el.find('.js-navProfileMenu');
    targ.siblings('.popMenu').addClass('hide');
    if(targ.hasClass('hide')){
      targ.removeClass('hide');
      $('#overlay').removeClass('fadeOut hide');
      $('html').on('click.closeNav', function(e){
        if($(e.target).closest(targ).length === 0){
          targ.addClass('hide');
          $('#overlay').addClass('fadeOut hide');
          $(this).off('.closeNav');
        }
      });
    }else{
      targ.addClass('hide');
      $('#overlay').addClass('fadeOut hide');
    }
  },

  navCloseClick: function(){
    "use strict";
    var process = remote.process;
    if (process.platform != 'darwin') {
      this.currentWindow.close();
    } else {
      this.currentWindow.hide();
    }
  },

  navMinClick: function(){
    "use strict";
    this.currentWindow.minimize();
  },

  navMaxClick: function(){
    "use strict";
    if(this.currentWindow.isMaximized()){
      this.currentWindow.unmaximize();
    } else {
      this.currentWindow.maximize();
    }
  },

  navBackClick: function(){
    "use strict";
    window.history.back();
  },

  navFwdClick: function(){
    "use strict";
    window.history.forward();
  },

  navRefreshClick: function(){
    "use strict";
    this.currentWindow.reload();
  },

  addressBarKeyup: function(e){
    "use strict";
    var barText = this.addressInput.val();
    if(barText.length > 0){
      //detect enter key
      if (e.keyCode == 13){
        this.addressBarProcess(barText);
        this.addressBarGoBtn.addClass("fadeOut");
      } else {
        this.addressBarGoBtn.removeClass("fadeOut");
        this.closeStatusBar();
      }
    } else {
      this.addressBarGoBtn.addClass("fadeOut");
      this.closeStatusBar();
    }
  },

  addressBarProcess: function(addressBarText){
    "use strict";
    var guid = "",
        handle = "",
        state = "",
        itemHash = "",
        addressTextArray = addressBarText.split("/");

    state = addressTextArray[1] ? "/" + addressTextArray[1] : "";
    itemHash = addressTextArray[2] ? "/" + addressTextArray[2] : "";

    if(addressTextArray[0].charAt(0) == "@"){
      handle = addressTextArray[0];
      this.showStatusBar('Navigation by handle is not supported yet.');
    } else if(addressTextArray[0].length === 40){
      guid = addressTextArray[0];
      Backbone.history.navigate('#userPage/' + guid + state + itemHash, {trigger:true});
    } else {
      this.showStatusBar('This is not a valid user GUID or handle.');
    }

  },

  showStatusBar: function(msgText){
    "use strict";
    this.statusBar.find('.js-statusBarMessage').text(msgText);
    this.statusBar.removeClass('fadeOut');
  },

  closeStatusBar: function(){
    "use strict";
    this.statusBar.addClass('fadeOut');
  },

  countrySelect: function(e){
    "use strict";
    var targ = $(e.currentTarget);
    var ctry = targ.attr('data-code');
    $('.js-homeModal-countryList').find('input[type=radio]').prop("checked", false);
    targ.find('input[type=radio]').prop("checked", true);
    this.model.set('country', ctry);
  },

  currencySelect: function(e){
    "use strict";
    var targ = $(e.currentTarget); //TODO: Rename variables to be more readable
    //var crcy = targ.attr('data-name');
    var ccode = targ.attr('data-code');
    $('.js-homeModal-currencyList').find('input[type=radio]').prop("checked", false);
    targ.find('input[type=radio]').prop("checked", true);
    //this.model.set('currency', crcy);
    this.model.set('currency_code', ccode);
  },

  languageSelect: function(e){
    "use strict";
    var targ = $(e.currentTarget); //TODO: Rename variables to be more readable
    var lang = targ.attr('data-code');
    $('.js-homeModal-languageList').find('input[type=radio]').prop("checked", false);
    targ.find('input[type=radio]').prop("checked", true);
    this.model.set('language', lang);
  },

  timeSelect: function(e){
    "use strict";
    var inpt = $(e.target).closest('input[type=radio]'); //TODO: Rename variables to be more readable
    var tz = inpt.attr('id');
    $('.js-homeModal-timezoneList').find('input[type=radio]').prop("checked", false);
    inpt.prop("checked", true);
    this.model.set('time_zone', tz);
  },

  newHandle: function(e){
    "use strict";
    this.$el.find('.js-homeModal-handleInput').closest('.flexRow').removeClass('hide');
    this.$el.find('.js-homeModal-existingHandle').parent().addClass('hide');
    this.$el.find('.js-homeModal-cancelHandle').parent().removeClass('hide');
  },

  existingHandle: function(e){
    "use strict";
    //TODO: add code to connect handle here
  },

  cancelHandle: function(e){
    "use strict";
    this.$el.find('.js-homeModal-handleInput').closest('.flexRow').addClass('hide');
    this.$el.find('.js-homeModal-existingHandle').parent().removeClass('hide');
    this.$el.find('.js-homeModal-cancelHandle').parent().addClass('hide');
  },

  uploadAvatar: function(e){
    "use strict";
    var self = this;
    var reader = new FileReader();
    reader.onload = function (e) {
      self.$el.find('.js-avatarPreview').css('background', 'url(' + e.target.result + ') 50% 50% / cover no-repeat');
      //self.model.set('tempAvatar', e.target.result);
      //TODO: add canvas resizing here
    };
    reader.readAsDataURL($(e.target)[0].files[0]);
  },

  settingsDone: function(e){
    "use strict";
    this.model.set('beenSet',true);
    this.$el.find('.js-homeModal').hide();

    // Start application walkthrough (coming soon once I have better designs)
  },

  closeModal: function(e){
    "use strict";
    $(e.target).closest('.modal').addClass('fadeOut');
    console.log('sdfdsf');
  },

  navAdminPanel: function(){
    "use strict";
    this.$el.find('.js-adminModal').removeClass('fadeOut');
    this.adminPanel.updatePage();
  },

  close: function(){
    "use strict";
    __.each(this.subViews, function(subView) {
      if(subView.close){
        subView.close();
      }else{
        subView.remove();
      }
    });
    this.remove();
  },

  setSelectedTheme: function(e){
    "use strict"
    // Needs to save to the object and update the dom
  },

  shadeColor2: function shadeColor2(color, percent) {
    var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
    return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
  }

});

<|endoftext|>


<commit_before>(function ($) {
    $.extend(true, window, {
        "Ext": {
            "Plugins": {
                "HeaderFilter": HeaderFilter
            }
        }
    });

    /*
    Based on SlickGrid Header Menu Plugin (https://github.com/mleibman/SlickGrid/blob/master/plugins/slick.headermenu.js)

    (Can't be used at the same time as the header menu plugin as it implements the dropdown in the same way)
    */

    function HeaderFilter(options) {
        var grid;
        var self = this;
        var handler = new Slick.EventHandler();
        var defaults = {
            buttonImage: "../images/down.png",
            filterImage: "../images/filter.png",
            sortAscImage: "../images/sort-asc.png",
            sortDescImage: "../images/sort-desc.png"
        };
        var $menu;

        function init(g) {
            options = $.extend(true, {}, defaults, options);
            grid = g;
            handler.subscribe(grid.onHeaderCellRendered, handleHeaderCellRendered)
                   .subscribe(grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy)
                   .subscribe(grid.onClick, handleBodyMouseDown)
                   .subscribe(grid.onColumnsResized, columnsResized);

            grid.setColumns(grid.getColumns());

            $(document.body).bind("mousedown", handleBodyMouseDown);
        }

        function destroy() {
            handler.unsubscribeAll();
            $(document.body).unbind("mousedown", handleBodyMouseDown);
        }

        function handleBodyMouseDown(e) {
            if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) {
                hideMenu();
            }
        }

        function hideMenu() {
            if ($menu) {
                $menu.remove();
                $menu = null;
            }
        }

        function handleHeaderCellRendered(e, args) {
            var column = args.column;

            var $el = $("

")
                .addClass("slick-header-menubutton")
                .data("column", column);

            if (options.buttonImage) {
                $el.css("background-image", "url(" + options.buttonImage + ")");
            }

            $el.bind("click", showFilter).appendTo(args.node);
        }

        function handleBeforeHeaderCellDestroy(e, args) {
            $(args.node)
                .find(".slick-header-menubutton")
                .remove();
        }

        function addMenuItem(menu, columnDef, title, command, image) {
            var $item = $("
")
                         .data("command", command)
                         .data("column", columnDef)
                         .bind("click", handleMenuItemClick)
                         .appendTo(menu);

            var $icon = $("
")
                         .appendTo($item);

            if (image) {
                $icon.css("background-image", "url(" + image + ")");
            }

            $("")
             .text(title)
             .appendTo($item);
        }

        function addMenuInput(menu, columnDef) {
            $("<input class='input' placeholder='Search' style='margin-top: 5px; width: 206px'>")
                .data("column", columnDef)
                .bind("keyup", function (e) {
                    var filterVals = getFilterValuesByInput($(this));
                    updateFilterInputs(menu, columnDef, filterVals);
                })
                .appendTo(menu);
        }

        function updateFilterInputs(menu, columnDef, filterItems) {
            var filterOptions = "<input type='checkbox' value='-1' />(Select All)";
            columnDef.filterValues = columnDef.filterValues || [];

            // WorkingFilters is a copy of the filters to enable apply/cancel behaviour
            workingFilters = columnDef.filterValues.slice(0);

            for (var i = 0; i < filterItems.length; i++) {
                var filtered = _.contains(workingFilters, filterItems[i]);

                filterOptions += "<input type='checkbox' value='" + i + "'"
                + (filtered ? " checked='checked'" : "")
                + "/>" + filterItems[i] + "";
            }
            var $filter = menu.find('.filter');
            $filter.empty().append($(filterOptions));

            $(':checkbox', $filter).bind('click', function () {
                workingFilters = changeWorkingFilter(filterItems, workingFilters, $(this));
            });
        }

        function showFilter(e) {
            var $menuButton = $(this);
            var columnDef = $menuButton.data("column");

            columnDef.filterValues = columnDef.filterValues || [];

            // WorkingFilters is a copy of the filters to enable apply/cancel behaviour
            var workingFilters = columnDef.filterValues.slice(0);

            var filterItems;

            if (workingFilters.length === 0) {
                // Filter based all available values
                filterItems = getFilterValues(grid.getData(), columnDef);
            }
            else {
                // Filter based on current dataView subset
                filterItems = getAllFilterValues(grid.getData().getItems(), columnDef);
            }

            if (!$menu) {
                $menu = $("
").appendTo(document.body);
            }

            $menu.empty();

            addMenuItem($menu, columnDef, 'Sort Ascending', 'sort-asc', options.sortAscImage);
            addMenuItem($menu, columnDef, 'Sort Descending', 'sort-desc', options.sortDescImage);
            addMenuInput($menu, columnDef);

            var filterOptions = "<input type='checkbox' value='-1' />(Select All)";

            for (var i = 0; i < filterItems.length; i++) {
                var filtered = _.contains(workingFilters, filterItems[i]);

                filterOptions += "<input type='checkbox' value='" + i + "'"
                                 + (filtered ? " checked='checked'" : "")
                                 + "/>" + filterItems[i] + "";
            }

            var $filter = $("
")
                           .append($(filterOptions))
                           .appendTo($menu);

            $('<button>OK</button>')
                .appendTo($menu)
                .bind('click', function (ev) {
                    columnDef.filterValues = workingFilters.splice(0);
                    setButtonImage($menuButton, columnDef.filterValues.length > 0);
                    handleApply(ev, columnDef);
                });

            $('<button>Clear</button>')
                .appendTo($menu)
                .bind('click', function (ev) {
                    columnDef.filterValues.length = 0;
                    setButtonImage($menuButton, false);
                    handleApply(ev, columnDef);
                });

            $('<button>Cancel</button>')
                .appendTo($menu)
                .bind('click', hideMenu);

            $(':checkbox', $filter).bind('click', function () {
                workingFilters = changeWorkingFilter(filterItems, workingFilters, $(this));
            });

            var offset = $(this).offset();
            var left = offset.left - $menu.width() + $(this).width() - 8;

            var menutop = offset.top + $(this).height();

            if (menutop + offset.top > $(window).height()) {
                menutop -= ($menu.height() + $(this).height() + 8);
            }
            $menu.css("top", menutop)
                 .css("left", (left > 0 ? left : 0));
        }

        function columnsResized() {
            hideMenu();
        }

        function changeWorkingFilter(filterItems, workingFilters, $checkbox) {
            var value = $checkbox.val();
            var $filter = $checkbox.parent().parent();

            if ($checkbox.val() < 0) {
                // Select All
                if ($checkbox.prop('checked')) {
                    $(':checkbox', $filter).prop('checked', true);
                    workingFilters = filterItems.slice(0);
                } else {
                    $(':checkbox', $filter).prop('checked', false);
                    workingFilters.length = 0;
                }
            } else {
                var index = _.indexOf(workingFilters, filterItems[value]);

                if ($checkbox.prop('checked') && index < 0) {
                    workingFilters.push(filterItems[value]);
                }
                else {
                    if (index > -1) {
                        workingFilters.splice(index, 1);
                    }
                }
            }

            return workingFilters;
        }

        function setButtonImage($el, filtered) {
            var image = "url(" + (filtered ? options.filterImage : options.buttonImage) + ")";

            $el.css("background-image", image);
        }

        function handleApply(e, columnDef) {
            hideMenu();

            self.onFilterApplied.notify({ "grid": grid, "column": columnDef }, e, self);

            e.preventDefault();
            e.stopPropagation();
        }

        function getFilterValues(dataView, column) {
            var seen = [];
            for (var i = 0; i < dataView.getLength() ; i++) {
                var value = dataView.getItem(i)[column.field];

                if (!_.contains(seen, value)) {
                    seen.push(value);
                }
            }

            return _.sortBy(seen, function (v) { return v; });
        }

        function getFilterValuesByInput($input) {
            var column = $input.data("column"),
                filter = $input.val(),
                dataView = grid.getData(),
                seen = [];

            for (var i = 0; i < dataView.getLength() ; i++) {
                var value = dataView.getItem(i)[column.field];

                if (filter.length > 0) {
                    var mVal = !value ? '' : value;
                    var lowercaseFilter = filter.toString().toLowerCase();
                    var lowercaseVal = mVal.toString().toLowerCase();
                    if (!_.contains(seen, value) && lowercaseVal.indexOf(lowercaseFilter) > -1) {
                        seen.push(value);
                    }
                }
                else {
                    if (!_.contains(seen, value)) {
                        seen.push(value);
                    }
                }
            }

            return _.sortBy(seen, function (v) { return v; });
        }

        function getAllFilterValues(data, column) {
            var seen = [];
            for (var i = 0; i < data.length; i++) {
                var value = data[i][column.field];

                if (!_.contains(seen, value)) {
                    seen.push(value);
                }
            }

            return _.sortBy(seen, function (v) { return v; });
        }

        function handleMenuItemClick(e) {
            var command = $(this).data("command");
            var columnDef = $(this).data("column");

            hideMenu();

            self.onCommand.notify({
                "grid": grid,
                "column": columnDef,
                "command": command
            }, e, self);

            e.preventDefault();
            e.stopPropagation();
        }

        $.extend(this, {
            "init": init,
            "destroy": destroy,
            "onFilterApplied": new Slick.Event(),
            "onCommand": new Slick.Event()
        });
    }
})(jQuery);<commit_msg>Added searchbox and grouping capabilities<commit_after>(function ($) {
    $.extend(true, window, {
        "Ext": {
            "Plugins": {
                "HeaderFilter": HeaderFilter
            }
        }
    });

    /*
    Based on SlickGrid Header Menu Plugin (https://github.com/mleibman/SlickGrid/blob/master/plugins/slick.headermenu.js)

    (Can't be used at the same time as the header menu plugin as it implements the dropdown in the same way)
    */

    function HeaderFilter(options) {
        var grid;
        var self = this;
        var handler = new Slick.EventHandler();
        var defaults = {
            buttonImage: "../images/down.png",
            filterImage: "../images/filter.png",
            sortAscImage: "../images/sort-asc.png",
            sortDescImage: "../images/sort-desc.png",
            useSearchBox: false,
            allowGrouping: false
        };
        var $menu;

        function init(g) {
            options = $.extend(true, {}, defaults, options);
            grid = g;
            handler.subscribe(grid.onHeaderCellRendered, handleHeaderCellRendered)
                   .subscribe(grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy)
                   .subscribe(grid.onClick, handleBodyMouseDown)
                   .subscribe(grid.onColumnsResized, columnsResized);

            grid.setColumns(grid.getColumns());

            $(document.body).bind("mousedown", handleBodyMouseDown);
        }

        function destroy() {
            handler.unsubscribeAll();
            $(document.body).unbind("mousedown", handleBodyMouseDown);
        }

        function handleBodyMouseDown(e) {
            if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) {
                hideMenu();
            }
        }

        function hideMenu() {
            if ($menu) {
                $menu.remove();
                $menu = null;
            }
        }

        function handleHeaderCellRendered(e, args) {
            var column = args.column;

            var $el = $("

")
                .addClass("slick-header-menubutton")
                .data("column", column);

            if (options.buttonImage) {
                $el.css("background-image", "url(" + options.buttonImage + ")");
            }

            $el.bind("click", showFilter).appendTo(args.node);
        }

        function handleBeforeHeaderCellDestroy(e, args) {
            $(args.node)
                .find(".slick-header-menubutton")
                .remove();
        }

        function addMenuItem(menu, columnDef, title, command, image, useFA) {
            var $item = $("
")
                         .data("command", command)
                         .data("column", columnDef)
                         .bind("click", handleMenuItemClick)
                         .appendTo(menu);

            var $icon = $("
")
                         .appendTo($item);

            if (image) {
                if (usefa)
                    $icon.addClass('fa').addClass(image);   // image is treated as a Font-Awesome class instead of an img src
                else
                    $icon.css("background-image", "url(" + image + ")");
            }

            $("")
             .text(title)
             .appendTo($item);
        }

        function addSearchBox(menu, columnDef, value) {
            var $searchBox = $('<input type="search" class="slick-search-box" placeholder="Search..." />')
                .data('column', columnDef)
                .data('command', 'search')
                .appendTo(menu)
                .wrap('

');

            if (typeof value == 'string') {
                $searchBox.val(value);
            }
        }

        function addMenuInput(menu, columnDef) {
            $("<input class='input' placeholder='Search' style='margin-top: 5px; width: 206px'>")
                .data("column", columnDef)
                .bind("keyup", function (e) {
                    var filterVals = getFilterValuesByInput($(this));
                    updateFilterInputs(menu, columnDef, filterVals);
                })
                .appendTo(menu);
        }

        function updateFilterInputs(menu, columnDef, filterItems) {
            var filterOptions = "<input type='checkbox' value='-1' />(Select All)";
            columnDef.filterValues = columnDef.filterValues || [];

            // WorkingFilters is a copy of the filters to enable apply/cancel behaviour
            workingFilters = columnDef.filterValues.slice(0);

            for (var i = 0; i < filterItems.length; i++) {
                var filtered = _.contains(workingFilters, filterItems[i]);

                filterOptions += "<input type='checkbox' value='" + i + "'"
                + (filtered ? " checked='checked'" : "")
                + "/>" + filterItems[i] + "";
            }
            var $filter = menu.find('.filter');
            $filter.empty().append($(filterOptions));

            $(':checkbox', $filter).bind('click', function () {
                workingFilters = changeWorkingFilter(filterItems, workingFilters, $(this));
            });
        }

        function showFilter(e) {
            var $menuButton = $(this);
            var columnDef = $menuButton.data("column");

            columnDef.filterValues = columnDef.filterValues || [];

            // WorkingFilters is a copy of the filters to enable apply/cancel behaviour
            var workingFilters = columnDef.filterValues.slice(0);

            var filterItems;

            if (workingFilters.length === 0) {
                // Filter based all available values
                filterItems = getFilterValues(grid.getData(), columnDef);
            }
            else {
                // Filter based on current dataView subset
                filterItems = getAllFilterValues(grid.getData().getItems(), columnDef);
            }

            if (!$menu) {
                $menu = $("
").appendTo(document.body);
            }

            $menu.empty();

            addMenuItem($menu, columnDef, 'Sort Ascending', 'sort-asc', options.sortAscImage);
            addMenuItem($menu, columnDef, 'Sort Descending', 'sort-desc', options.sortDescImage);

            if (options.allowGrouping) {
                if (isColumnGrouped(columnDef)) {
                    addMenuItem($menu, columnDef, 'Un-Group', 'ungroup', 'fa-unlink', true);
                } else {
                    addMenuItem($menu, columnDef, 'Group By', 'group', 'fa-link', true);
                }
            }

            if (options.useSearchBox) {
                addSearchBox($menu, columnDef, workingFilters);
            }

            addMenuInput($menu, columnDef);

            var filterOptions = "<input type='checkbox' value='-1' />(Select All)";

            for (var i = 0; i < filterItems.length; i++) {
                var filtered = _.contains(workingFilters, filterItems[i]);

                filterOptions += "<input type='checkbox' value='" + i + "'"
                                 + (filtered ? " checked='checked'" : "")
                                 + "/>" + filterItems[i] + "";
            }

            var $filter = $("
")
                           .append($(filterOptions))
                           .appendTo($menu);

            if (typeof workingFilters == 'string')
                $filter.hide();

            $('<button>OK</button>')
                .appendTo($menu)
                .bind('click', function (ev) {
                    columnDef.filterValues = (workingFilters.splice) ? workingFilters.splice(0) : workingFilters;
                    setButtonImage($menuButton, columnDef.filterValues.length > 0);
                    handleApply(ev, columnDef);
                });

            $('<button>Clear</button>')
                .appendTo($menu)
                .bind('click', function (ev) {
                    columnDef.filterValues = [];
                    setButtonImage($menuButton, false);
                    handleApply(ev, columnDef);
                });

            $('<button>Cancel</button>')
                .appendTo($menu)
                .bind('click', hideMenu);

            $(':checkbox', $filter).bind('click', function () {
                workingFilters = changeWorkingFilter(filterItems, workingFilters, $(this));
            });

            var setCheckboxVisibility = function () {
                if (this.value.length)
                    $filter.slideUp();
                else
                    $filter.slideDown();
            };

            $('input.slick-search-box', $menu)
                .keyup(setCheckboxVisibility)
                .bind('search', setCheckboxVisibility).blur(function () {
                if (this.value) {
                    workingFilters = this.value;
                } else {
                    workingFilters = [];
                    $(':checkbox[checked]', $filter).each(function (idx, item) {
                        workingFilters.push($(item).val());
                    });
                }
            });

            var offset = $(this).offset();
            var left = offset.left - $menu.width() + $(this).width() - 8;
            var top = offset.top + $(this).height();
            var maxHeight = $(window).height() - top - (parseInt($menu.css('padding').replace('px', '')) * 2);

            $menu.css("top", top)
                 .css("left", (left > 0 ? left : 0));

            if (maxHeight < $menu.outerHeight()) {
                $menu.css("overflow-y", "scroll").css("height", maxHeight);
            }
        }

        function columnsResized() {
            hideMenu();
        }

        function changeWorkingFilter(filterItems, workingFilters, $checkbox) {
            var value = $checkbox.val();
            var $filter = $checkbox.parent().parent();

            if ($checkbox.val() < 0) {
                // Select All
                if ($checkbox.prop('checked')) {
                    $(':checkbox', $filter).prop('checked', true);
                    workingFilters = filterItems.slice(0);
                } else {
                    $(':checkbox', $filter).prop('checked', false);
                    workingFilters.length = 0;
                }
            } else {
                var index = _.indexOf(workingFilters, filterItems[value]);

                if ($checkbox.prop('checked') && index < 0) {
                    workingFilters.push(filterItems[value]);
                }
                else {
                    if (index > -1) {
                        workingFilters.splice(index, 1);
                    }
                }
            }

            return workingFilters;
        }

        function setButtonImage($el, filtered) {
            var image = "url(" + (filtered ? options.filterImage : options.buttonImage) + ")";

            $el.css("background-image", image);
        }

        function handleApply(e, columnDef) {
            hideMenu();

            self.onFilterApplied.notify({ "grid": grid, "column": columnDef }, e, self);

            e.preventDefault();
            e.stopPropagation();
        }

        function getFilterValues(dataView, column) {
            var seen = [];
            for (var i = 0; i < dataView.getLength() ; i++) {
                var value = dataView.getItem(i)[column.field];

                if (!_.contains(seen, value)) {
                    seen.push(value);
                }
            }

            return _.sortBy(seen, function (v) { return v; });
        }

        function getFilterValuesByInput($input) {
            var column = $input.data("column"),
                filter = $input.val(),
                dataView = grid.getData(),
                seen = [];

            for (var i = 0; i < dataView.getLength() ; i++) {
                var value = dataView.getItem(i)[column.field];

                if (filter.length > 0) {
                    var mVal = !value ? '' : value;
                    var lowercaseFilter = filter.toString().toLowerCase();
                    var lowercaseVal = mVal.toString().toLowerCase();
                    if (!_.contains(seen, value) && lowercaseVal.indexOf(lowercaseFilter) > -1) {
                        seen.push(value);
                    }
                }
                else {
                    if (!_.contains(seen, value)) {
                        seen.push(value);
                    }
                }
            }

            return _.sortBy(seen, function (v) { return v; });
        }

        function getAllFilterValues(data, column) {
            var seen = [];
            for (var i = 0; i < data.length; i++) {
                var value = data[i][column.field];

                if (!_.contains(seen, value)) {
                    seen.push(value);
                }
            }

            return _.sortBy(seen, function (v) { return v; });
        }

        function handleMenuItemClick(e) {
            var command = $(this).data("command");
            var columnDef = $(this).data("column");

            hideMenu();

            self.onCommand.notify({
                "grid": grid,
                "column": columnDef,
                "command": command
            }, e, self);

            e.preventDefault();
            e.stopPropagation();
        }

        $.extend(this, {
            "init": init,
            "destroy": destroy,
            "onFilterApplied": new Slick.Event(),
            "onCommand": new Slick.Event()
        });
    }
})(jQuery);
<|endoftext|>


<commit_before>'use strict'

const fs = require('fs')
const path = require('path')

/**
 * Attempt to dereference the given ref without requiring a Git environment
 * to be present. Note that this method will not be able to dereference packed
 * refs but should suffice for simple refs like 'HEAD'.
 *
 * Will throw an error for unborn HEAD.
 *
 * @param {string} gitDir The path to the Git repository's .git directory
 * @param {string} ref    A qualified git ref such as 'HEAD' or 'refs/heads/master'
 */
function revParse(gitDir, ref) {
  const refPath = path.join(gitDir, ref)
  // eslint-disable-next-line no-sync
  const refContents = fs.readFileSync(refPath)
  const refRe = /^([a-f0-9]{40})|(?:ref: (refs\/.*))$/m
  const refMatch = refRe.exec(refContents)

  if (!refMatch) {
    throw new Error(
      `Could not de-reference HEAD to SHA, invalid ref in ${refPath}: ${refContents}`
    )
  }

  return refMatch[1] || revParse(gitDir, refMatch[2])
}

function getSHA() {
  // CircleCI does some funny stuff where HEAD points to an packed ref, but
  // luckily it gives us the SHA we want in the environment.
  const circleSHA = process.env.CIRCLE_SHA1
  if (circleSHA) {
    return circleSHA
  }

  return revParse(path.resolve(__dirname, '../.git'), 'HEAD')
}

module.exports = {
  getSHA,
}
<commit_msg>report better error when ref cannot be found<commit_after>'use strict'

const fs = require('fs')
const path = require('path')

/**
 * Attempt to dereference the given ref without requiring a Git environment
 * to be present. Note that this method will not be able to dereference packed
 * refs but should suffice for simple refs like 'HEAD'.
 *
 * Will throw an error for unborn HEAD.
 *
 * @param {string} gitDir The path to the Git repository's .git directory
 * @param {string} ref    A qualified git ref such as 'HEAD' or 'refs/heads/master'
 */
function revParse(gitDir, ref) {
  const refPath = path.join(gitDir, ref)

  try {
    // eslint-disable-next-line no-sync
    fs.statSync(refPath)
  } catch (err) {
    throw new Error(
      `Could not de-reference HEAD to SHA, ref does not exist on disk: ${refPath}`
    )
  }
  // eslint-disable-next-line no-sync
  const refContents = fs.readFileSync(refPath)
  const refRe = /^([a-f0-9]{40})|(?:ref: (refs\/.*))$/m
  const refMatch = refRe.exec(refContents)

  if (!refMatch) {
    throw new Error(
      `Could not de-reference HEAD to SHA, invalid ref in ${refPath}: ${refContents}`
    )
  }

  return refMatch[1] || revParse(gitDir, refMatch[2])
}

function getSHA() {
  // CircleCI does some funny stuff where HEAD points to an packed ref, but
  // luckily it gives us the SHA we want in the environment.
  const circleSHA = process.env.CIRCLE_SHA1
  if (circleSHA) {
    return circleSHA
  }

  return revParse(path.resolve(__dirname, '../.git'), 'HEAD')
}

module.exports = {
  getSHA,
}
<|endoftext|>


<commit_before>const nest = require('depnest')
const { h } = require('mutant')

exports.gives = nest('app.html.app')

exports.needs = nest({
  'app.html.tabs': 'first',
  'app.page.errors': 'first',
  'app.sync.goTo': 'first',
  'app.sync.initialise': 'first',
  'app.sync.window': 'reduce',
  'history.obs.location': 'first',
  'history.sync.push': 'first',
  'settings.sync.get': 'first'
})

exports.create = function (api) {
  return nest('app.html.app', app)

  function app () {
    console.log('STARTING app')

    window = api.app.sync.window(window)

    const initialTabs = api.settings.sync.get('patchbay.defaultTabs')
    const App = h('App', api.app.html.tabs(initialTabs))

    api.app.sync.initialise(App)
    // runs all the functions in app/sync/initialise

    return App
  }
}

<commit_msg>Make close work again<commit_after>const nest = require('depnest')
const { h } = require('mutant')

exports.gives = nest('app.html.app')

exports.needs = nest({
  'app.html.tabs': 'first',
  'app.page.errors': 'first',
  'app.sync.goTo': 'first',
  'app.sync.initialise': 'first',
  'app.sync.window': 'reduce',
  'history.obs.location': 'first',
  'history.sync.push': 'first',
  'settings.sync.get': 'first'
})

exports.create = function (api) {
  return nest('app.html.app', app)

  function app () {
    console.log('STARTING app')

    window = api.app.sync.window(window)

    const initialTabs = api.settings.sync.get('patchbay.defaultTabs')
    const App = h('App', api.app.html.tabs(initialTabs))

    api.app.sync.initialise(App)
    // runs all the functions in app/sync/initialise

    api.history.obs.location()(loc => {
      if (!api.app.html.tabs().currentPage()) // close
        api.app.sync.goTo(loc || {})
    })

    return App
  }
}

<|endoftext|>


<commit_before>/*
 * Beard Functionality
 * Matthew Evans, Autumn 2013
 * No License
 *
 */
;(function($){

	var Beard = {

		// Initialize the player
		init: function (elm) {

			// Cache the element as a jQuery object
			var $elm = $(elm);

			// Cache other DOM elements
			Beard.elements             = {};
			Beard.elements.$player     = $elm;
			Beard.elements.$text       = $elm.find('.text');
			Beard.elements.$time       = $elm.find('.time');
			Beard.elements.$progress   = $elm.find('.progress');
			Beard.elements.$seek       = $elm.find('.seek');
			Beard.elements.$seekCursor = $elm.find('.seek .cursor');
			Beard.elements.$play       = $elm.find('.play');

			// Instantiate a new Audio object
			Beard.audio = new Audio();

			// Set the source of the audio object if the data-source attribute exists
			if($elm.attr('data-source')) {
				Beard.setSource($elm.attr('data-source'));
			}

			// Bind Audio events
			$(Beard.audio).on('timeupdate', Beard.displayTime);
			$(Beard.audio).on('timeupdate', Beard.displayProgress);
			$(Beard.audio).on('canplay', Beard.displayTime);

			// Bind DOM events
			Beard.elements.$player.on('click', '.play', Beard.play);
			Beard.elements.$seek.on('mousemove', Beard.placeSeekCursor);
			Beard.elements.$seek.on('click', 'a', Beard.seek);

		},
		load: function (options) {

			// Must have a valid source
			if(!Beard.parseValidAudioFormat(options.url)) {
				Beard.error('Invalid audio url.');
				return;
			}

			// Stop audio
			Beard.pause();

			// Set the values
			Beard.displayText(options.text);
			Beard.setSource(options.url);

		},
		error: function (message) {

			// Cache current message
			var prevMessage = Beard.elements.$text.text();

			// Display the error message
			Beard.displayText('Error: ' + message);

			// After 3 seconds, return to the previous message
			setInterval(function(){
				Beard.displayText(prevMessage);
			}, 3000)

		},
		setSource: function (url) {

			// Set the audio source to the new URL
			Beard.audio.src = url;

		},
		displayProgress: function () {

			// Cache the current and total time
			var currentTime = Math.round(Beard.audio.currentTime);
			var totalTime = Math.round(Beard.audio.duration);

			// Compute the percentage out of 100
			var progressPercent = ( currentTime / totalTime ) * 100;

			// Set the width of the progress bar as a percent value
			Beard.elements.$progress.css('width', progressPercent + '%');

		},
		displayText: function (text) {

			// Inject text into the text display element
			Beard.elements.$text.text(text);

		},
		displayTime: function () {

			// Parse the time properties fromt the audio object
			var currentTime = Beard.parseSecondsToReadableFormat(Beard.audio.currentTime);
			var duration = '';
			if(Beard.audio.duration) {
				duration = ' of ' + Beard.parseSecondsToReadableFormat(Beard.audio.duration);
			} else {
				currentTime = 'loading...';
			}

			// Create a time string
			var timeString = currentTime + duration;

			// Update the view
			Beard.elements.$time.text(timeString);

		},
		parseSecondsToReadableFormat: function (seconds) {

			// Trim the decimals from the time
			var trimmedSeconds = Math.round(seconds);

			// If it's less than one minute
			if(trimmedSeconds < 60) { return '0:' + ( '0' + trimmedSeconds).slice(-2) };

			// If it is a multiple of one minute
			if(trimmedSeconds % 60 == 0) { return  ( trimmedSeconds / 60 ) + ':00'; }

			// If it is more than one minute
			return Math.round(seconds/60) + ':' + ('0' + Math.round(seconds % 60)).slice(-2);

		},
		parseValidAudioFormat: function (url) {

			// Allowable audio formats
			var whitelist = ['mp3', 'ogg', 'mp4'];

			// Valid is false by default
			var valid = false;

			// With the url...
			if(url) {
				// Check to make sure that the format exists in the whitelist
				var splitUrl = url.split('.');
				var ext = splitUrl[splitUrl.length - 1];
				if( $.inArray(ext, whitelist) >= 0 ) {
					// If it exists, it's valid
					valid = true;
				}
			}

			return valid;

		},
		computeMouseOffsetX: function (e) {

			var parentOffset = $(e.target).parent().offset();
			return e.pageX - parentOffset.left;

		},
		play: function () {

			// We use the same buton for play and pause
			if(Beard.playing) {

				return Beard.pause();

			}

			// Start audio
			Beard.audio.play();

			// Set a playing property
			Beard.playing = true;

			// Set the DOM element to playing
			Beard.elements.$player.addClass('playing');

			// Set the text
			Beard.elements.$play.text('Pause');

		},
		pause: function () {

			// Pause audio
			Beard.audio.pause();

			// Unset the playing property
			Beard.playing = false;

			// Unset the DOM playing class
			Beard.elements.$player.removeClass('playing');

			// Set the text
			Beard.elements.$play.text('Play');

		},
		seek: function (e) {

			console.log(e);

			// Divide the width of the player by the positon of the cursor.
			// Use that percentage to place the currentTime of the track.
			var beardWidth = Beard.elements.$player.width();
			var mouseX = Beard.computeMouseOffsetX(e);

			var seekPercentage = ( mouseX / beardWidth );
			var duration = Beard.audio.duration;
			var seekTime = duration * seekPercentage;

			Beard.audio.currentTime = seekTime;

		},
		placeSeekCursor: function (e) {

			// Place the seek cursor directly on the mouse cursor when hovered.
			var mouseX = Beard.computeMouseOffsetX(e);
			Beard.elements.$seekCursor.css('left', mouseX);

		}
	};

	// Expose the functions as collection methods.
	$.extend($.fn, {

		// This method initializes values from attributes and binds events.
		beard: function (options) {

			// We only support one player element (for now)
			Beard.init(this[0]);

			// If this is for testing purposes we return the Beard object instead.
			if(options && options.internals === true) {
				return Beard;
			}

			// Otherwise we return the jQuery object for chainability.
			return this;

		},
		// Load a new source URL
		load: function (options) {

			Beard.load(options);
			return this;

		}

	});

})(jQuery);<commit_msg>Prevented default so no page jumping<commit_after>/*
 * Beard Functionality
 * Matthew Evans, Autumn 2013
 * No License
 *
 */
;(function($){

	var Beard = {

		// Initialize the player
		init: function (elm) {

			// Cache the element as a jQuery object
			var $elm = $(elm);

			// Cache other DOM elements
			Beard.elements             = {};
			Beard.elements.$player     = $elm;
			Beard.elements.$text       = $elm.find('.text');
			Beard.elements.$time       = $elm.find('.time');
			Beard.elements.$progress   = $elm.find('.progress');
			Beard.elements.$seek       = $elm.find('.seek');
			Beard.elements.$seekCursor = $elm.find('.seek .cursor');
			Beard.elements.$play       = $elm.find('.play');

			// Instantiate a new Audio object
			Beard.audio = new Audio();

			// Set the source of the audio object if the data-source attribute exists
			if($elm.attr('data-source')) {
				Beard.setSource($elm.attr('data-source'));
			}

			// Bind Audio events
			$(Beard.audio).on('timeupdate', Beard.displayTime);
			$(Beard.audio).on('timeupdate', Beard.displayProgress);
			$(Beard.audio).on('canplay', Beard.displayTime);

			// Bind DOM events
			Beard.elements.$player.on('click', '.play', Beard.play);
			Beard.elements.$seek.on('mousemove', Beard.placeSeekCursor);
			Beard.elements.$seek.on('click', 'a', Beard.seek);

		},
		load: function (options) {

			// Must have a valid source
			if(!Beard.parseValidAudioFormat(options.url)) {
				Beard.error('Invalid audio url.');
				return;
			}

			// Stop audio
			Beard.pause();

			// Set the values
			Beard.displayText(options.text);
			Beard.setSource(options.url);

		},
		error: function (message) {

			// Cache current message
			var prevMessage = Beard.elements.$text.text();

			// Display the error message
			Beard.displayText('Error: ' + message);

			// After 3 seconds, return to the previous message
			setInterval(function(){
				Beard.displayText(prevMessage);
			}, 3000)

		},
		setSource: function (url) {

			// Set the audio source to the new URL
			Beard.audio.src = url;

		},
		displayProgress: function () {

			// Cache the current and total time
			var currentTime = Math.round(Beard.audio.currentTime);
			var totalTime = Math.round(Beard.audio.duration);

			// Compute the percentage out of 100
			var progressPercent = ( currentTime / totalTime ) * 100;

			// Set the width of the progress bar as a percent value
			Beard.elements.$progress.css('width', progressPercent + '%');

		},
		displayText: function (text) {

			// Inject text into the text display element
			Beard.elements.$text.text(text);

		},
		displayTime: function () {

			// Parse the time properties fromt the audio object
			var currentTime = Beard.parseSecondsToReadableFormat(Beard.audio.currentTime);
			var duration = '';
			if(Beard.audio.duration) {
				duration = ' of ' + Beard.parseSecondsToReadableFormat(Beard.audio.duration);
			} else {
				currentTime = 'loading...';
			}

			// Create a time string
			var timeString = currentTime + duration;

			// Update the view
			Beard.elements.$time.text(timeString);

		},
		parseSecondsToReadableFormat: function (seconds) {

			// Trim the decimals from the time
			var trimmedSeconds = Math.round(seconds);

			// If it's less than one minute
			if(trimmedSeconds < 60) { return '0:' + ( '0' + trimmedSeconds).slice(-2) };

			// If it is a multiple of one minute
			if(trimmedSeconds % 60 == 0) { return  ( trimmedSeconds / 60 ) + ':00'; }

			// If it is more than one minute
			return Math.round(seconds/60) + ':' + ('0' + Math.round(seconds % 60)).slice(-2);

		},
		parseValidAudioFormat: function (url) {

			// Allowable audio formats
			var whitelist = ['mp3', 'ogg', 'mp4'];

			// Valid is false by default
			var valid = false;

			// With the url...
			if(url) {
				// Check to make sure that the format exists in the whitelist
				var splitUrl = url.split('.');
				var ext = splitUrl[splitUrl.length - 1];
				if( $.inArray(ext, whitelist) >= 0 ) {
					// If it exists, it's valid
					valid = true;
				}
			}

			return valid;

		},
		computeMouseOffsetX: function (e) {

			var parentOffset = $(e.target).parent().offset();
			return e.pageX - parentOffset.left;

		},
		play: function () {

			// We use the same buton for play and pause
			if(Beard.playing) {

				return Beard.pause();

			}

			// Start audio
			Beard.audio.play();

			// Set a playing property
			Beard.playing = true;

			// Set the DOM element to playing
			Beard.elements.$player.addClass('playing');

			// Set the text
			Beard.elements.$play.text('Pause');

		},
		pause: function () {

			// Pause audio
			Beard.audio.pause();

			// Unset the playing property
			Beard.playing = false;

			// Unset the DOM playing class
			Beard.elements.$player.removeClass('playing');

			// Set the text
			Beard.elements.$play.text('Play');

		},
		seek: function (e) {

			// Stop the default action/page jumping.
			e.preventDefault();

			// Divide the width of the player by the positon of the cursor.
			// Use that percentage to place the currentTime of the track.
			var beardWidth = Beard.elements.$player.width();
			var mouseX = Beard.computeMouseOffsetX(e);

			var seekPercentage = ( mouseX / beardWidth );
			var duration = Beard.audio.duration;
			var seekTime = duration * seekPercentage;

			Beard.audio.currentTime = seekTime;

		},
		placeSeekCursor: function (e) {

			// Place the seek cursor directly on the mouse cursor when hovered.
			var mouseX = Beard.computeMouseOffsetX(e);
			Beard.elements.$seekCursor.css('left', mouseX);

		}
	};

	// Expose the functions as collection methods.
	$.extend($.fn, {

		// This method initializes values from attributes and binds events.
		beard: function (options) {

			// We only support one player element (for now)
			Beard.init(this[0]);

			// If this is for testing purposes we return the Beard object instead.
			if(options && options.internals === true) {
				return Beard;
			}

			// Otherwise we return the jQuery object for chainability.
			return this;

		},
		// Load a new source URL
		load: function (options) {

			Beard.load(options);
			return this;

		}

	});

})(jQuery);<|endoftext|>