<commit_before>(function() {

    'use strict';

    /* eslint-disable angular/no-service-method */
    angular.module('facetApp')

    .service('nlpService', nlpService);

    /* @ngInject */
    function nlpService($q, _, AdvancedSparqlService, FacetResultHandler, SPARQL_ENDPOINT_URL,
            facetService, objectMapperService) {

        /* Public API */

        // Get the results based on facet selections.
        // Return a promise.
        this.getResults = getResults;
        this.getStatistics = getStatistics;
        // Get the facets.
        // Return a promise (because of translation).
        this.getFacets = facetService.getFacets;
        // Get the facet options.
        // Return an object.
        this.getFacetOptions = getFacetOptions;

        this.upos = ['VERB', 'NOUN', 'ADJ', 'PROPN'];

        /* Implementation */

        var prefixes =
            ' PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ' +
            ' PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> ' +
            ' PREFIX dct: <http://purl.org/dc/terms/> ' +
            ' PREFIX skos: <http://www.w3.org/2004/02/skos/core#> ' +
            ' PREFIX skosxl: <http://www.w3.org/2008/05/skos-xl#> ' +
            ' PREFIX xml: <http://www.w3.org/XML/1998/namespace> ' +
            ' PREFIX bioc: <http://ldf.fi/schema/bioc/> ' +
            ' PREFIX gvp: <http://vocab.getty.edu/ontology#> ' +
            ' PREFIX nbf: <http://ldf.fi/nbf/> ' +
            ' PREFIX categories: <http://ldf.fi/nbf/categories/> ' +
            ' PREFIX crm: <http://www.cidoc-crm.org/cidoc-crm/> ' +
            ' PREFIX foaf: <http://xmlns.com/foaf/0.1/> ' +
            ' PREFIX conll: <http://ufal.mff.cuni.cz/conll2009-st/task-description.html#> ' +
            ' PREFIX nif: <http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#> ' +
            ' PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> ';

        // The query for the results.
        // ?id is bound to the person URI.
        var query =
            ' SELECT DISTINCT ?id WHERE {' +
            '  { ' +
            '    <RESULT_SET> ' +
            '  } ' +
            ' }';

        var lemmaQry = prefixes +
            ' SELECT DISTINCT ?lemma (COUNT(?lemma) AS ?count) { ' +
            '  VALUES ?doc { <DOC> } ' +
            '  ?s nif:structure/<http://ldf.fi/nbf/biography/data#bioId> ?doc . ' +
            '  ?s a nif:Sentence . ' +
            '  ?word nif:sentence ?s . ' +
            '  ?word conll:UPOS "<UPOS>" . ' +
            '  ?word conll:LEMMA ?lemma . ' +
            '  FILTER(STRLEN(STR(?lemma))>1) ' +
            ' } GROUP BY ?lemma ORDER BY DESC(?count) LIMIT 50';

        var docCountByDecadeQry = prefixes +
            ' SELECT DISTINCT ?year (COUNT(distinct ?id) AS ?count) { ' +
            '  { ' +
            '    <RESULT_SET> ' +
            '  } ' +
            '  ?id foaf:focus/^crm:P98_brought_into_life/nbf:time/gvp:estStart ?birth . ' +
            '  BIND (FLOOR(YEAR(?birth)/10)*10 AS ?year) ' +
            ' } GROUP BY ?year ORDER BY ?year ';

        var nbfEndpointConfig = {
            'endpointUrl': SPARQL_ENDPOINT_URL,
            'usePost': true
        };

        var nlpEndpointConfig = {
            'endpointUrl': 'http://ldf.fi/nbf-nlp/sparql',
            'usePost': true
        };

        var facetOptions = {
            endpointUrl: nbfEndpointConfig.endpointUrl,
            rdfClass: '<http://ldf.fi/nbf/PersonConcept>',
            constraint: '?id <http://xmlns.com/foaf/0.1/focus>/<http://ldf.fi/nbf/has_biography> [] .',
            preferredLang : 'fi',
            noSelectionString: '-- Ei valintaa --'
        };

        // This handler is for the additional queries.
        var endpoint = new AdvancedSparqlService(nlpEndpointConfig, objectMapperService);
        var nbfEndpoint = new AdvancedSparqlService(nbfEndpointConfig, objectMapperService);

        function getStatistics(facetSelections) {
            var qry = docCountByDecadeQry.replace(/<RESULT_SET>/g, facetSelections.constraint.join(' '));
            return nbfEndpoint.getObjectsNoGrouping(qry);
        }

        function getResults(facetSelections) {
            var self = this;
            var qry = query.replace(/<RESULT_SET>/g, facetSelections.constraint.join(' '));
            return nbfEndpoint.getObjectsNoGrouping(qry).then(function(results) {
                if (results.length > 10000) {
                    return $q.reject({
                        message: 'Tulosjoukko on liian suuri. Ole hyvä ja rajaa tuloksia suodittimien avulla'
                    });
                }
                var promises = {};
                var uposQry = lemmaQry.replace(/<DOC>/g, '<' + _.map(results, 'id').join('> <') + '>');

                self.upos.forEach(function(upos) {
                    promises[upos] = endpoint.getObjectsNoGrouping(uposQry.replace(/<UPOS>/g, upos));
                });

                return $q.all(promises);
            });
        }

        function getFacetOptions() {
            return facetOptions;
        }

    }
})();
<commit_msg>Use https for NLP endpoint<commit_after>(function() {

    'use strict';

    /* eslint-disable angular/no-service-method */
    angular.module('facetApp')

    .service('nlpService', nlpService);

    /* @ngInject */
    function nlpService($q, _, AdvancedSparqlService, FacetResultHandler, SPARQL_ENDPOINT_URL,
            facetService, objectMapperService) {

        /* Public API */

        // Get the results based on facet selections.
        // Return a promise.
        this.getResults = getResults;
        this.getStatistics = getStatistics;
        // Get the facets.
        // Return a promise (because of translation).
        this.getFacets = facetService.getFacets;
        // Get the facet options.
        // Return an object.
        this.getFacetOptions = getFacetOptions;

        this.upos = ['VERB', 'NOUN', 'ADJ', 'PROPN'];

        /* Implementation */

        var prefixes =
            ' PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ' +
            ' PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> ' +
            ' PREFIX dct: <http://purl.org/dc/terms/> ' +
            ' PREFIX skos: <http://www.w3.org/2004/02/skos/core#> ' +
            ' PREFIX skosxl: <http://www.w3.org/2008/05/skos-xl#> ' +
            ' PREFIX xml: <http://www.w3.org/XML/1998/namespace> ' +
            ' PREFIX bioc: <http://ldf.fi/schema/bioc/> ' +
            ' PREFIX gvp: <http://vocab.getty.edu/ontology#> ' +
            ' PREFIX nbf: <http://ldf.fi/nbf/> ' +
            ' PREFIX categories: <http://ldf.fi/nbf/categories/> ' +
            ' PREFIX crm: <http://www.cidoc-crm.org/cidoc-crm/> ' +
            ' PREFIX foaf: <http://xmlns.com/foaf/0.1/> ' +
            ' PREFIX conll: <http://ufal.mff.cuni.cz/conll2009-st/task-description.html#> ' +
            ' PREFIX nif: <http://persistence.uni-leipzig.org/nlp2rdf/ontologies/nif-core#> ' +
            ' PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> ';

        // The query for the results.
        // ?id is bound to the person URI.
        var query =
            ' SELECT DISTINCT ?id WHERE {' +
            '  { ' +
            '    <RESULT_SET> ' +
            '  } ' +
            ' }';

        var lemmaQry = prefixes +
            ' SELECT DISTINCT ?lemma (COUNT(?lemma) AS ?count) { ' +
            '  VALUES ?doc { <DOC> } ' +
            '  ?s nif:structure/<http://ldf.fi/nbf/biography/data#bioId> ?doc . ' +
            '  ?s a nif:Sentence . ' +
            '  ?word nif:sentence ?s . ' +
            '  ?word conll:UPOS "<UPOS>" . ' +
            '  ?word conll:LEMMA ?lemma . ' +
            '  FILTER(STRLEN(STR(?lemma))>1) ' +
            ' } GROUP BY ?lemma ORDER BY DESC(?count) LIMIT 50';

        var docCountByDecadeQry = prefixes +
            ' SELECT DISTINCT ?year (COUNT(distinct ?id) AS ?count) { ' +
            '  { ' +
            '    <RESULT_SET> ' +
            '  } ' +
            '  ?id foaf:focus/^crm:P98_brought_into_life/nbf:time/gvp:estStart ?birth . ' +
            '  BIND (FLOOR(YEAR(?birth)/10)*10 AS ?year) ' +
            ' } GROUP BY ?year ORDER BY ?year ';

        var nbfEndpointConfig = {
            'endpointUrl': SPARQL_ENDPOINT_URL,
            'usePost': true
        };

        var nlpEndpointConfig = {
            'endpointUrl': 'https://ldf.fi/nbf-nlp/sparql',
            'usePost': true
        };

        var facetOptions = {
            endpointUrl: nbfEndpointConfig.endpointUrl,
            rdfClass: '<http://ldf.fi/nbf/PersonConcept>',
            constraint: '?id <http://xmlns.com/foaf/0.1/focus>/<http://ldf.fi/nbf/has_biography> [] .',
            preferredLang : 'fi',
            noSelectionString: '-- Ei valintaa --'
        };

        // This handler is for the additional queries.
        var endpoint = new AdvancedSparqlService(nlpEndpointConfig, objectMapperService);
        var nbfEndpoint = new AdvancedSparqlService(nbfEndpointConfig, objectMapperService);

        function getStatistics(facetSelections) {
            var qry = docCountByDecadeQry.replace(/<RESULT_SET>/g, facetSelections.constraint.join(' '));
            return nbfEndpoint.getObjectsNoGrouping(qry);
        }

        function getResults(facetSelections) {
            var self = this;
            var qry = query.replace(/<RESULT_SET>/g, facetSelections.constraint.join(' '));
            return nbfEndpoint.getObjectsNoGrouping(qry).then(function(results) {
                if (results.length > 10000) {
                    return $q.reject({
                        message: 'Tulosjoukko on liian suuri. Ole hyvä ja rajaa tuloksia suodittimien avulla'
                    });
                }
                var promises = {};
                var uposQry = lemmaQry.replace(/<DOC>/g, '<' + _.map(results, 'id').join('> <') + '>');

                self.upos.forEach(function(upos) {
                    promises[upos] = endpoint.getObjectsNoGrouping(uposQry.replace(/<UPOS>/g, upos));
                });

                return $q.all(promises);
            });
        }

        function getFacetOptions() {
            return facetOptions;
        }

    }
})();
<|endoftext|>


<commit_before>'use strict';

angular.module('mgcrea.ngStrap.scrollspy', ['mgcrea.ngStrap.helpers.debounce', 'mgcrea.ngStrap.helpers.dimensions'])

  .provider('$scrollspy', function() {

    // Pool of registered spies
    var spies = this.$$spies = {};

    var defaults = this.defaults = {
      debounce: 150,
      throttle: 100,
      offset: 100
    };

    this.$get = function($window, $document, $rootScope, dimensions, debounce, throttle) {

      var windowEl = angular.element($window);
      var docEl = angular.element($document.prop('documentElement'));
      var bodyEl = angular.element($window.document.body);

      // Helper functions

      function nodeName(element, name) {
        return element[0].nodeName && element[0].nodeName.toLowerCase() === name.toLowerCase();
      }

      function ScrollSpyFactory(config) {

        // Common vars
        var options = angular.extend({}, defaults, config);
        if(!options.element) options.element = bodyEl;
        var isWindowSpy = nodeName(options.element, 'body');
        var scrollEl = isWindowSpy ? windowEl : options.element;
        var scrollId = isWindowSpy ? 'window' : options.id;

        // Use existing spy
        if(spies[scrollId]) {
          spies[scrollId].$$count++;
          return spies[scrollId];
        }

        var $scrollspy = {};

        // Private vars
        var unbindViewContentLoaded, unbindIncludeContentLoaded;
        var trackedElements = $scrollspy.$trackedElements = [];
        var sortedElements = [];
        var activeTarget;
        var debouncedCheckPosition;
        var throttledCheckPosition;
        var debouncedCheckOffsets;
        var viewportHeight;
        var scrollTop;

        $scrollspy.init = function() {

          // Setup internal ref counter
          this.$$count = 1;

          // Bind events
          debouncedCheckPosition = debounce(this.checkPosition, options.debounce);
          throttledCheckPosition = throttle(this.checkPosition, options.throttle);
          scrollEl.on('click', this.checkPositionWithEventLoop);
          windowEl.on('resize', debouncedCheckPosition);
          scrollEl.on('scroll', throttledCheckPosition);

          debouncedCheckOffsets = debounce(this.checkOffsets, options.debounce);
          unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', debouncedCheckOffsets);
          unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', debouncedCheckOffsets);
          debouncedCheckOffsets();

          // Register spy for reuse
          if(scrollId) {
            spies[scrollId] = $scrollspy;
          }

        };

        $scrollspy.destroy = function() {

          // Check internal ref counter
          this.$$count--;
          if(this.$$count > 0) {
            return;
          }

          // Unbind events
          scrollEl.off('click', this.checkPositionWithEventLoop);
          windowEl.off('resize', debouncedCheckPosition);
          scrollEl.off('scroll', debouncedCheckPosition);
          unbindViewContentLoaded();
          unbindIncludeContentLoaded();
          if (scrollId) {
            delete spies[scrollId];
          }
        };

        $scrollspy.checkPosition = function() {

          // Not ready yet
          if(!sortedElements.length) return;

          // Calculate the scroll position
          scrollTop = (isWindowSpy ? $window.pageYOffset : scrollEl.prop('scrollTop')) || 0;

          // Calculate the viewport height for use by the components
          viewportHeight = Math.max($window.innerHeight, docEl.prop('clientHeight'));

          // Activate first element if scroll is smaller
          if(scrollTop < sortedElements[0].offsetTop && activeTarget !== sortedElements[0].target) {
            return $scrollspy.$activateElement(sortedElements[0]);
          }

          // Activate proper element
          for (var i = sortedElements.length; i--;) {
            if(angular.isUndefined(sortedElements[i].offsetTop) || sortedElements[i].offsetTop === null) continue;
            if(activeTarget === sortedElements[i].target) continue;
            if(scrollTop < sortedElements[i].offsetTop) continue;
            if(sortedElements[i + 1] && scrollTop > sortedElements[i + 1].offsetTop) continue;
            return $scrollspy.$activateElement(sortedElements[i]);
          }

        };

        $scrollspy.checkPositionWithEventLoop = function() {
          // IE 9 throws an error if we use 'this' instead of '$scrollspy'
          // in this setTimeout call
          setTimeout($scrollspy.checkPosition, 1);
        };

        // Protected methods

        $scrollspy.$activateElement = function(element) {
          if(activeTarget) {
            var activeElement = $scrollspy.$getTrackedElement(activeTarget);
            if(activeElement) {
              activeElement.source.removeClass('active');
              if(nodeName(activeElement.source, 'li') && nodeName(activeElement.source.parent().parent(), 'li')) {
                activeElement.source.parent().parent().removeClass('active');
              }
            }
          }
          activeTarget = element.target;
          element.source.addClass('active');
          if(nodeName(element.source, 'li') && nodeName(element.source.parent().parent(), 'li')) {
            element.source.parent().parent().addClass('active');
          }
        };

        $scrollspy.$getTrackedElement = function(target) {
          return trackedElements.filter(function(obj) {
            return obj.target === target;
          })[0];
        };

        // Track offsets behavior

        $scrollspy.checkOffsets = function() {

          angular.forEach(trackedElements, function(trackedElement) {
            var targetElement = document.querySelector(trackedElement.target);
            trackedElement.offsetTop = targetElement ? dimensions.offset(targetElement).top : null;
            if(options.offset && trackedElement.offsetTop !== null) trackedElement.offsetTop -= options.offset * 1;
          });

          sortedElements = trackedElements
          .filter(function(el) {
            return el.offsetTop !== null;
          })
          .sort(function(a, b) {
            return a.offsetTop - b.offsetTop;
          });

          debouncedCheckPosition();

        };

        $scrollspy.trackElement = function(target, source) {
          trackedElements.push({target: target, source: source});
        };

        $scrollspy.untrackElement = function(target, source) {
          var toDelete;
          for (var i = trackedElements.length; i--;) {
            if(trackedElements[i].target === target && trackedElements[i].source === source) {
              toDelete = i;
              break;
            }
          }
          trackedElements = trackedElements.splice(toDelete, 1);
        };

        $scrollspy.activate = function(i) {
          trackedElements[i].addClass('active');
        };

        // Initialize plugin

        $scrollspy.init();
        return $scrollspy;

      }

      return ScrollSpyFactory;

    };

  })

  .directive('bsScrollspy', function($rootScope, debounce, dimensions, $scrollspy) {

    return {
      restrict: 'EAC',
      link: function postLink(scope, element, attr) {

        var options = {scope: scope};
        angular.forEach(['offset', 'target'], function(key) {
          if(angular.isDefined(attr[key])) options[key] = attr[key];
        });

        var scrollspy = $scrollspy(options);
        scrollspy.trackElement(options.target, element);

        scope.$on('$destroy', function() {
          if (scrollspy) {
            scrollspy.untrackElement(options.target, element);
            scrollspy.destroy();
          }
          options = null;
          scrollspy = null;
        });

      }
    };

  })

  .directive('bsScrollspyList', function($rootScope, debounce, dimensions, $scrollspy) {

    return {
      restrict: 'A',
      compile: function postLink(element, attr) {
        var children = element[0].querySelectorAll('li > a[href]');
        angular.forEach(children, function(child) {
          var childEl = angular.element(child);
          childEl.parent().attr('bs-scrollspy', '').attr('data-target', childEl.attr('href'));
        });
      }

    };

  });
<commit_msg>fix(scrollSpy) destroy scroll event handler<commit_after>'use strict';

angular.module('mgcrea.ngStrap.scrollspy', ['mgcrea.ngStrap.helpers.debounce', 'mgcrea.ngStrap.helpers.dimensions'])

  .provider('$scrollspy', function() {

    // Pool of registered spies
    var spies = this.$$spies = {};

    var defaults = this.defaults = {
      debounce: 150,
      throttle: 100,
      offset: 100
    };

    this.$get = function($window, $document, $rootScope, dimensions, debounce, throttle) {

      var windowEl = angular.element($window);
      var docEl = angular.element($document.prop('documentElement'));
      var bodyEl = angular.element($window.document.body);

      // Helper functions

      function nodeName(element, name) {
        return element[0].nodeName && element[0].nodeName.toLowerCase() === name.toLowerCase();
      }

      function ScrollSpyFactory(config) {

        // Common vars
        var options = angular.extend({}, defaults, config);
        if(!options.element) options.element = bodyEl;
        var isWindowSpy = nodeName(options.element, 'body');
        var scrollEl = isWindowSpy ? windowEl : options.element;
        var scrollId = isWindowSpy ? 'window' : options.id;

        // Use existing spy
        if(spies[scrollId]) {
          spies[scrollId].$$count++;
          return spies[scrollId];
        }

        var $scrollspy = {};

        // Private vars
        var unbindViewContentLoaded, unbindIncludeContentLoaded;
        var trackedElements = $scrollspy.$trackedElements = [];
        var sortedElements = [];
        var activeTarget;
        var debouncedCheckPosition;
        var throttledCheckPosition;
        var debouncedCheckOffsets;
        var viewportHeight;
        var scrollTop;

        $scrollspy.init = function() {

          // Setup internal ref counter
          this.$$count = 1;

          // Bind events
          debouncedCheckPosition = debounce(this.checkPosition, options.debounce);
          throttledCheckPosition = throttle(this.checkPosition, options.throttle);
          scrollEl.on('click', this.checkPositionWithEventLoop);
          windowEl.on('resize', debouncedCheckPosition);
          scrollEl.on('scroll', throttledCheckPosition);

          debouncedCheckOffsets = debounce(this.checkOffsets, options.debounce);
          unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', debouncedCheckOffsets);
          unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', debouncedCheckOffsets);
          debouncedCheckOffsets();

          // Register spy for reuse
          if(scrollId) {
            spies[scrollId] = $scrollspy;
          }

        };

        $scrollspy.destroy = function() {

          // Check internal ref counter
          this.$$count--;
          if(this.$$count > 0) {
            return;
          }

          // Unbind events
          scrollEl.off('click', this.checkPositionWithEventLoop);
          windowEl.off('resize', debouncedCheckPosition);
          scrollEl.off('scroll', throttledCheckPosition);
          unbindViewContentLoaded();
          unbindIncludeContentLoaded();
          if (scrollId) {
            delete spies[scrollId];
          }
        };

        $scrollspy.checkPosition = function() {

          // Not ready yet
          if(!sortedElements.length) return;

          // Calculate the scroll position
          scrollTop = (isWindowSpy ? $window.pageYOffset : scrollEl.prop('scrollTop')) || 0;

          // Calculate the viewport height for use by the components
          viewportHeight = Math.max($window.innerHeight, docEl.prop('clientHeight'));

          // Activate first element if scroll is smaller
          if(scrollTop < sortedElements[0].offsetTop && activeTarget !== sortedElements[0].target) {
            return $scrollspy.$activateElement(sortedElements[0]);
          }

          // Activate proper element
          for (var i = sortedElements.length; i--;) {
            if(angular.isUndefined(sortedElements[i].offsetTop) || sortedElements[i].offsetTop === null) continue;
            if(activeTarget === sortedElements[i].target) continue;
            if(scrollTop < sortedElements[i].offsetTop) continue;
            if(sortedElements[i + 1] && scrollTop > sortedElements[i + 1].offsetTop) continue;
            return $scrollspy.$activateElement(sortedElements[i]);
          }

        };

        $scrollspy.checkPositionWithEventLoop = function() {
          // IE 9 throws an error if we use 'this' instead of '$scrollspy'
          // in this setTimeout call
          setTimeout($scrollspy.checkPosition, 1);
        };

        // Protected methods

        $scrollspy.$activateElement = function(element) {
          if(activeTarget) {
            var activeElement = $scrollspy.$getTrackedElement(activeTarget);
            if(activeElement) {
              activeElement.source.removeClass('active');
              if(nodeName(activeElement.source, 'li') && nodeName(activeElement.source.parent().parent(), 'li')) {
                activeElement.source.parent().parent().removeClass('active');
              }
            }
          }
          activeTarget = element.target;
          element.source.addClass('active');
          if(nodeName(element.source, 'li') && nodeName(element.source.parent().parent(), 'li')) {
            element.source.parent().parent().addClass('active');
          }
        };

        $scrollspy.$getTrackedElement = function(target) {
          return trackedElements.filter(function(obj) {
            return obj.target === target;
          })[0];
        };

        // Track offsets behavior

        $scrollspy.checkOffsets = function() {

          angular.forEach(trackedElements, function(trackedElement) {
            var targetElement = document.querySelector(trackedElement.target);
            trackedElement.offsetTop = targetElement ? dimensions.offset(targetElement).top : null;
            if(options.offset && trackedElement.offsetTop !== null) trackedElement.offsetTop -= options.offset * 1;
          });

          sortedElements = trackedElements
          .filter(function(el) {
            return el.offsetTop !== null;
          })
          .sort(function(a, b) {
            return a.offsetTop - b.offsetTop;
          });

          debouncedCheckPosition();

        };

        $scrollspy.trackElement = function(target, source) {
          trackedElements.push({target: target, source: source});
        };

        $scrollspy.untrackElement = function(target, source) {
          var toDelete;
          for (var i = trackedElements.length; i--;) {
            if(trackedElements[i].target === target && trackedElements[i].source === source) {
              toDelete = i;
              break;
            }
          }
          trackedElements = trackedElements.splice(toDelete, 1);
        };

        $scrollspy.activate = function(i) {
          trackedElements[i].addClass('active');
        };

        // Initialize plugin

        $scrollspy.init();
        return $scrollspy;

      }

      return ScrollSpyFactory;

    };

  })

  .directive('bsScrollspy', function($rootScope, debounce, dimensions, $scrollspy) {

    return {
      restrict: 'EAC',
      link: function postLink(scope, element, attr) {

        var options = {scope: scope};
        angular.forEach(['offset', 'target'], function(key) {
          if(angular.isDefined(attr[key])) options[key] = attr[key];
        });

        var scrollspy = $scrollspy(options);
        scrollspy.trackElement(options.target, element);

        scope.$on('$destroy', function() {
          if (scrollspy) {
            scrollspy.untrackElement(options.target, element);
            scrollspy.destroy();
          }
          options = null;
          scrollspy = null;
        });

      }
    };

  })

  .directive('bsScrollspyList', function($rootScope, debounce, dimensions, $scrollspy) {

    return {
      restrict: 'A',
      compile: function postLink(element, attr) {
        var children = element[0].querySelectorAll('li > a[href]');
        angular.forEach(children, function(child) {
          var childEl = angular.element(child);
          childEl.parent().attr('bs-scrollspy', '').attr('data-target', childEl.attr('href'));
        });
      }

    };

  });
<|endoftext|>


<commit_before>import {
  REQUEST_GET_JKF, RECEIVE_GET_JKF, REQUEST_PUT_JKF, RECEIVE_PUT_JKF,
  FAIL_PUT_JKF, CLEAR_MESSAGE, CHANGE_AUTO_SAVE,
  MOVE_PIECE, CHANGE_COMMENTS, CHANGE_REVERSED,
  GOTO_PATH, GO_BACK, GO_FORWARD, MOVE_UP_FORK, MOVE_DOWN_FORK, REMOVE_FORK
} from './actions';
import { jkfToKifuTree, kifuTreeToJKF } from "./treeUtils";
import { buildJKFPlayerFromState } from './playerUtils';

const initialJKF = { header: {}, moves: [{}] };
const initialState = {
  jkf: initialJKF,
  kifuTree: jkfToKifuTree(initialJKF),
  reversed: false,
  currentPathArray: [],
  fetching: false,
  autoSaveEnabled: false,
  needSave: false,
};

export default function kifuTree(state = initialState, action) {
  switch (action.type) {
    case REQUEST_GET_JKF: {
      return Object.assign({}, state, { fetching: true });
    }
    case RECEIVE_GET_JKF: {
      const jkf = action.jkf;
      const tree = jkfToKifuTree(jkf);

      return Object.assign({}, state, initialState, { kifuTree: tree, jkf: jkf, fetching: false });
    }
    case REQUEST_PUT_JKF: {
      return Object.assign({}, state, { message: "Saving...", needSave: false });
    }
    case RECEIVE_PUT_JKF: {
      return Object.assign({}, state, { message: "Saved" });
    }
    case FAIL_PUT_JKF: {
      return Object.assign({}, state, { message: "Failed" });
    }
    case CLEAR_MESSAGE: {
      return Object.assign({}, state, { message: "" });
    }
    case CHANGE_AUTO_SAVE: {
      const enabled = action.enabled;
      return Object.assign({}, state, { autoSaveEnabled: enabled });
    }
    case MOVE_PIECE: {
      const player = buildJKFPlayerFromState(state);
      const { jkf, kifuTree } = state;
      const move = action.move;
      move.to = { x: move.to.x, y: move.to.y }; // In some environment, move.to contains dropEffect attribute. Get rid of it.
      const originalJKFString = JSON.stringify(jkf);

      try {
        if (!player.inputMove(move)) {
          move.promote = confirm("成りますか？");
          player.inputMove(move);
        }
      } catch (e) {
        alert(e);
        return state;
      }

      const newJKF = player.kifu;
      const newJKFString = JSON.stringify(newJKF);
      const changed = originalJKFString !== newJKFString;
      //console.log(changed);

      if (changed) {
        const newKifuTree = jkfToKifuTree(newJKF);
        const newPathArray = findCurrentPathArray(newKifuTree, player);
        return Object.assign({}, state, { kifuTree: newKifuTree, currentPathArray: newPathArray, jkf: newJKF, needSave: true });
      }

      const newPathArray = findCurrentPathArray(kifuTree, player);
      return Object.assign({}, state, { currentPathArray: newPathArray });
    }
    case GOTO_PATH: {
      return Object.assign({}, state, { currentPathArray: action.pathArray });
    }
    case GO_BACK: {
      const { currentPathArray } = state;
      if (currentPathArray.length === 0) {
        return state;
      }
      const newPathArray = currentPathArray.slice(0, -1);
      return Object.assign({}, state, { currentPathArray: newPathArray });
    }
    case GO_FORWARD: {
      const { kifuTree, currentPathArray } = state;
      const currentNode = findNodeByPath(kifuTree, currentPathArray);
      if (currentNode.children.length === 0) {
        return state;
      }
      const newPathArray = [...currentPathArray, 0];
      return Object.assign({}, state, { currentPathArray: newPathArray });
    }
    case CHANGE_COMMENTS: {
      const { kifuTree, currentPathArray, jkf } = state;
      const value = action.value;

      const { clonedTree, lastNode } = cloneTreeUntil(kifuTree, currentPathArray);
      lastNode.comment = value;
      const newJKF = kifuTreeToJKF(clonedTree, jkf);

      return Object.assign({}, state, { kifuTree: clonedTree, jkf: newJKF, needSave: true });
    }
    case CHANGE_REVERSED: {
      const value = action.value;
      return Object.assign({}, state, { reversed: value });
    }
    case MOVE_UP_FORK: {
      const { kifuTree, currentPathArray, jkf } = state;
      const pathArray = action.pathArray;

      const clonedTree = moveUpFork(kifuTree, pathArray);
      if (clonedTree === kifuTree) {
        return state;
      }
      const currentStringPathArray = getStringPathArray(kifuTree, currentPathArray);
      const newPathArray = getPathArray(clonedTree, currentStringPathArray);
      const newJKF = kifuTreeToJKF(clonedTree, jkf);

      return Object.assign({}, state, { kifuTree: clonedTree, currentPathArray: newPathArray, jkf: newJKF, needSave: true });
    }
    case MOVE_DOWN_FORK: {
      const { kifuTree, currentPathArray, jkf } = state;
      const pathArray = action.pathArray;

      const clonedTree = moveDownFork(kifuTree, pathArray);
      if (clonedTree === kifuTree) {
        return state;
      }
      const currentStringPathArray = getStringPathArray(kifuTree, currentPathArray);
      const newPathArray = getPathArray(clonedTree, currentStringPathArray);
      const newJKF = kifuTreeToJKF(clonedTree, jkf);

      return Object.assign({}, state, { kifuTree: clonedTree, currentPathArray: newPathArray, jkf: newJKF, needSave: true });
    }
    case REMOVE_FORK: {
      const { kifuTree, currentPathArray, jkf } = state;
      const pathArray = action.pathArray;

      const clonedTree = removeFork(kifuTree, pathArray);

      const currentStringPathArray = getStringPathArray(kifuTree, currentPathArray);
      const newPathArray = getPathArray(clonedTree, currentStringPathArray);
      const newJKF = kifuTreeToJKF(clonedTree, jkf);

      return Object.assign({}, state, { kifuTree: clonedTree, currentPathArray: newPathArray, jkf: newJKF, needSave: true });
    }
    default:
      return state;
  }
};

function moveUpFork(tree, pathArray) {
  if (pathArray.length === 0) {
    return tree; // do nothing
  }
  const lastNum = pathArray[pathArray.length - 1];
  const pathArrayOfParent = pathArray.slice(0, pathArray.length - 1);
  if (lastNum === 0) {
    return tree; // do nothing
  }

  const { clonedTree, lastNode } = cloneTreeUntil(tree, pathArrayOfParent);

  lastNode.children = [
    ...lastNode.children.slice(0, lastNum - 1),
    lastNode.children[lastNum],
    lastNode.children[lastNum - 1],
    ...lastNode.children.slice(lastNum + 1),
  ];

  return clonedTree;
}

function moveDownFork(tree, pathArray) {
  if (pathArray.length === 0) {
    return tree; // do nothing
  }
  const lastNum = pathArray[pathArray.length - 1];
  const pathArrayOfParent = pathArray.slice(0, pathArray.length - 1);
  if (lastNum === findNodeByPath(tree, pathArrayOfParent).children.length - 1) {
    return tree; // do nothing
  }

  const { clonedTree, lastNode } = cloneTreeUntil(tree, pathArrayOfParent);

  lastNode.children = [
    ...lastNode.children.slice(0, lastNum),
    lastNode.children[lastNum + 1],
    lastNode.children[lastNum],
    ...lastNode.children.slice(lastNum + 2),
  ];

  return clonedTree;
}

function removeFork(tree, pathArray) {
  if (pathArray.length === 0) {
    return tree; // do nothing
  }
  const lastNum = pathArray[pathArray.length - 1];
  const pathArrayOfParent = pathArray.slice(0, pathArray.length - 1);

  const { clonedTree, lastNode } = cloneTreeUntil(tree, pathArrayOfParent);

  lastNode.children = [
    ...lastNode.children.slice(0, lastNum),
    ...lastNode.children.slice(lastNum + 1),
  ];

  return clonedTree;
}

function cloneTreeUntil(tree, pathArray) {
  const clonedTree = Object.assign({}, tree);

  let currentNode = clonedTree;
  pathArray.forEach(num => {
    const childNode = Object.assign({}, currentNode.children[num]);
    currentNode.children = [
      ...currentNode.children.slice(0, num),
      childNode,
      ...currentNode.children.slice(num + 1),
    ];

    currentNode = childNode;
  });

  return { clonedTree: clonedTree, lastNode: currentNode };
}

// Find current path array from tree depending on the state of player
function findCurrentPathArray(tree, player, stopIfMissing = false) {
  const pathArray = [];
  let currentNode = tree;
  for (let state of player.getReadableKifuState().slice(1, player.tesuu + 1)) {
    const nextNodeIndex = currentNode.children.findIndex(childNode => childNode.readableKifu === state.kifu);
    const nextNode = currentNode.children[nextNodeIndex];
    if (!nextNode) {
      if (stopIfMissing) {
        break;
      } else {
        throw new Error("Active node not found");
      }
    }

    pathArray.push(nextNodeIndex);
    currentNode = nextNode;
  }
  return pathArray;
}

function findNodeByPath(tree, pathArray) {
  let currentNode = tree;
  pathArray.forEach(num => {
    currentNode = currentNode.children[num];
  });

  return currentNode;
}

function getStringPathArray(tree, pathArray) {
  const stringPathArray = [];
  let currentNode = tree;
  for (let index of pathArray) {
    const nextNode = currentNode.children[index];
    stringPathArray.push(nextNode.readableKifu);
    currentNode = nextNode;
  }

  return stringPathArray;
}

function getPathArray(tree, stringPathArray) {
  const pathArray = [];
  let currentNode = tree;
  for (let kifu of stringPathArray) {
    const nextNodeIndex = currentNode.children.findIndex(childNode => childNode.readableKifu === kifu);
    const nextNode = currentNode.children[nextNodeIndex];
    if (!nextNode) {
      break;  // stop if node is missing (e.g. node is removed)
    }
    pathArray.push(nextNodeIndex);
    currentNode = nextNode;
  }

  return pathArray;
}
<commit_msg>Simply ignore when drag and drop to the same place<commit_after>import {
  REQUEST_GET_JKF, RECEIVE_GET_JKF, REQUEST_PUT_JKF, RECEIVE_PUT_JKF,
  FAIL_PUT_JKF, CLEAR_MESSAGE, CHANGE_AUTO_SAVE,
  MOVE_PIECE, CHANGE_COMMENTS, CHANGE_REVERSED,
  GOTO_PATH, GO_BACK, GO_FORWARD, MOVE_UP_FORK, MOVE_DOWN_FORK, REMOVE_FORK
} from './actions';
import { jkfToKifuTree, kifuTreeToJKF } from "./treeUtils";
import { buildJKFPlayerFromState } from './playerUtils';

const initialJKF = { header: {}, moves: [{}] };
const initialState = {
  jkf: initialJKF,
  kifuTree: jkfToKifuTree(initialJKF),
  reversed: false,
  currentPathArray: [],
  fetching: false,
  autoSaveEnabled: false,
  needSave: false,
};

export default function kifuTree(state = initialState, action) {
  switch (action.type) {
    case REQUEST_GET_JKF: {
      return Object.assign({}, state, { fetching: true });
    }
    case RECEIVE_GET_JKF: {
      const jkf = action.jkf;
      const tree = jkfToKifuTree(jkf);

      return Object.assign({}, state, initialState, { kifuTree: tree, jkf: jkf, fetching: false });
    }
    case REQUEST_PUT_JKF: {
      return Object.assign({}, state, { message: "Saving...", needSave: false });
    }
    case RECEIVE_PUT_JKF: {
      return Object.assign({}, state, { message: "Saved" });
    }
    case FAIL_PUT_JKF: {
      return Object.assign({}, state, { message: "Failed" });
    }
    case CLEAR_MESSAGE: {
      return Object.assign({}, state, { message: "" });
    }
    case CHANGE_AUTO_SAVE: {
      const enabled = action.enabled;
      return Object.assign({}, state, { autoSaveEnabled: enabled });
    }
    case MOVE_PIECE: {
      const player = buildJKFPlayerFromState(state);
      const { jkf, kifuTree } = state;
      const move = action.move;
      move.to = { x: move.to.x, y: move.to.y }; // In some environment, move.to contains dropEffect attribute. Get rid of it.
      if (move.from && move.from.x === move.to.x && move.from.y === move.to.y) {
        return state; // drop to the same place. do nothing
      }
      const originalJKFString = JSON.stringify(jkf);

      try {
        if (!player.inputMove(move)) {
          move.promote = confirm("成りますか？");
          player.inputMove(move);
        }
      } catch (e) {
        alert(e);
        return state;
      }

      const newJKF = player.kifu;
      const newJKFString = JSON.stringify(newJKF);
      const changed = originalJKFString !== newJKFString;
      //console.log(changed);

      if (changed) {
        const newKifuTree = jkfToKifuTree(newJKF);
        const newPathArray = findCurrentPathArray(newKifuTree, player);
        return Object.assign({}, state, { kifuTree: newKifuTree, currentPathArray: newPathArray, jkf: newJKF, needSave: true });
      }

      const newPathArray = findCurrentPathArray(kifuTree, player);
      return Object.assign({}, state, { currentPathArray: newPathArray });
    }
    case GOTO_PATH: {
      return Object.assign({}, state, { currentPathArray: action.pathArray });
    }
    case GO_BACK: {
      const { currentPathArray } = state;
      if (currentPathArray.length === 0) {
        return state;
      }
      const newPathArray = currentPathArray.slice(0, -1);
      return Object.assign({}, state, { currentPathArray: newPathArray });
    }
    case GO_FORWARD: {
      const { kifuTree, currentPathArray } = state;
      const currentNode = findNodeByPath(kifuTree, currentPathArray);
      if (currentNode.children.length === 0) {
        return state;
      }
      const newPathArray = [...currentPathArray, 0];
      return Object.assign({}, state, { currentPathArray: newPathArray });
    }
    case CHANGE_COMMENTS: {
      const { kifuTree, currentPathArray, jkf } = state;
      const value = action.value;

      const { clonedTree, lastNode } = cloneTreeUntil(kifuTree, currentPathArray);
      lastNode.comment = value;
      const newJKF = kifuTreeToJKF(clonedTree, jkf);

      return Object.assign({}, state, { kifuTree: clonedTree, jkf: newJKF, needSave: true });
    }
    case CHANGE_REVERSED: {
      const value = action.value;
      return Object.assign({}, state, { reversed: value });
    }
    case MOVE_UP_FORK: {
      const { kifuTree, currentPathArray, jkf } = state;
      const pathArray = action.pathArray;

      const clonedTree = moveUpFork(kifuTree, pathArray);
      if (clonedTree === kifuTree) {
        return state;
      }
      const currentStringPathArray = getStringPathArray(kifuTree, currentPathArray);
      const newPathArray = getPathArray(clonedTree, currentStringPathArray);
      const newJKF = kifuTreeToJKF(clonedTree, jkf);

      return Object.assign({}, state, { kifuTree: clonedTree, currentPathArray: newPathArray, jkf: newJKF, needSave: true });
    }
    case MOVE_DOWN_FORK: {
      const { kifuTree, currentPathArray, jkf } = state;
      const pathArray = action.pathArray;

      const clonedTree = moveDownFork(kifuTree, pathArray);
      if (clonedTree === kifuTree) {
        return state;
      }
      const currentStringPathArray = getStringPathArray(kifuTree, currentPathArray);
      const newPathArray = getPathArray(clonedTree, currentStringPathArray);
      const newJKF = kifuTreeToJKF(clonedTree, jkf);

      return Object.assign({}, state, { kifuTree: clonedTree, currentPathArray: newPathArray, jkf: newJKF, needSave: true });
    }
    case REMOVE_FORK: {
      const { kifuTree, currentPathArray, jkf } = state;
      const pathArray = action.pathArray;

      const clonedTree = removeFork(kifuTree, pathArray);

      const currentStringPathArray = getStringPathArray(kifuTree, currentPathArray);
      const newPathArray = getPathArray(clonedTree, currentStringPathArray);
      const newJKF = kifuTreeToJKF(clonedTree, jkf);

      return Object.assign({}, state, { kifuTree: clonedTree, currentPathArray: newPathArray, jkf: newJKF, needSave: true });
    }
    default:
      return state;
  }
};

function moveUpFork(tree, pathArray) {
  if (pathArray.length === 0) {
    return tree; // do nothing
  }
  const lastNum = pathArray[pathArray.length - 1];
  const pathArrayOfParent = pathArray.slice(0, pathArray.length - 1);
  if (lastNum === 0) {
    return tree; // do nothing
  }

  const { clonedTree, lastNode } = cloneTreeUntil(tree, pathArrayOfParent);

  lastNode.children = [
    ...lastNode.children.slice(0, lastNum - 1),
    lastNode.children[lastNum],
    lastNode.children[lastNum - 1],
    ...lastNode.children.slice(lastNum + 1),
  ];

  return clonedTree;
}

function moveDownFork(tree, pathArray) {
  if (pathArray.length === 0) {
    return tree; // do nothing
  }
  const lastNum = pathArray[pathArray.length - 1];
  const pathArrayOfParent = pathArray.slice(0, pathArray.length - 1);
  if (lastNum === findNodeByPath(tree, pathArrayOfParent).children.length - 1) {
    return tree; // do nothing
  }

  const { clonedTree, lastNode } = cloneTreeUntil(tree, pathArrayOfParent);

  lastNode.children = [
    ...lastNode.children.slice(0, lastNum),
    lastNode.children[lastNum + 1],
    lastNode.children[lastNum],
    ...lastNode.children.slice(lastNum + 2),
  ];

  return clonedTree;
}

function removeFork(tree, pathArray) {
  if (pathArray.length === 0) {
    return tree; // do nothing
  }
  const lastNum = pathArray[pathArray.length - 1];
  const pathArrayOfParent = pathArray.slice(0, pathArray.length - 1);

  const { clonedTree, lastNode } = cloneTreeUntil(tree, pathArrayOfParent);

  lastNode.children = [
    ...lastNode.children.slice(0, lastNum),
    ...lastNode.children.slice(lastNum + 1),
  ];

  return clonedTree;
}

function cloneTreeUntil(tree, pathArray) {
  const clonedTree = Object.assign({}, tree);

  let currentNode = clonedTree;
  pathArray.forEach(num => {
    const childNode = Object.assign({}, currentNode.children[num]);
    currentNode.children = [
      ...currentNode.children.slice(0, num),
      childNode,
      ...currentNode.children.slice(num + 1),
    ];

    currentNode = childNode;
  });

  return { clonedTree: clonedTree, lastNode: currentNode };
}

// Find current path array from tree depending on the state of player
function findCurrentPathArray(tree, player, stopIfMissing = false) {
  const pathArray = [];
  let currentNode = tree;
  for (let state of player.getReadableKifuState().slice(1, player.tesuu + 1)) {
    const nextNodeIndex = currentNode.children.findIndex(childNode => childNode.readableKifu === state.kifu);
    const nextNode = currentNode.children[nextNodeIndex];
    if (!nextNode) {
      if (stopIfMissing) {
        break;
      } else {
        throw new Error("Active node not found");
      }
    }

    pathArray.push(nextNodeIndex);
    currentNode = nextNode;
  }
  return pathArray;
}

function findNodeByPath(tree, pathArray) {
  let currentNode = tree;
  pathArray.forEach(num => {
    currentNode = currentNode.children[num];
  });

  return currentNode;
}

function getStringPathArray(tree, pathArray) {
  const stringPathArray = [];
  let currentNode = tree;
  for (let index of pathArray) {
    const nextNode = currentNode.children[index];
    stringPathArray.push(nextNode.readableKifu);
    currentNode = nextNode;
  }

  return stringPathArray;
}

function getPathArray(tree, stringPathArray) {
  const pathArray = [];
  let currentNode = tree;
  for (let kifu of stringPathArray) {
    const nextNodeIndex = currentNode.children.findIndex(childNode => childNode.readableKifu === kifu);
    const nextNode = currentNode.children[nextNodeIndex];
    if (!nextNode) {
      break;  // stop if node is missing (e.g. node is removed)
    }
    pathArray.push(nextNodeIndex);
    currentNode = nextNode;
  }

  return pathArray;
}
<|endoftext|>


<commit_before>/*!
 * Copyright 2014 mpowers
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
(function(window, undefined) {

	/**
	 * Composer manages an entry composer form, submitting its contents to the
	 * model when needed. Variants can just replace or extend this object.
	 */
	Composer = window.Composer = function(form) {
		this.form = $(form);
		var self = this;
		this.form.submit(function(e) {
			e.preventDefault();
			self.onSubmit();
		});
		this.form.find(".submit").click(function(e) {
			e.preventDefault();
			self.form.submit();
		});

		var attach = this.form.find(".attach");
		var input = this.form.find("input[type='file']");
		attach.click(function(e) {
			e.preventDefault();
			input[0].value = ""; // clear any preexisting value
			input.change();
			input.click();
		});
		input.on('change', function(e) {
			// grab file name so the UI can display it
			var value = input[0].value;
			if (value !== undefined) {
				var i = value.lastIndexOf('\\');
				if (i !== -1) {
					value = value.substring(i + 1);
				}
				attach.attr("file", value);
			}
		});
	};
	Composer.prototype = new Composer();
	Composer.prototype.constructor = Composer;

	/**
	 * Adds feed to container, replacing any existing element for this feed.
	 * Returns the added feed element.
	 */
	Composer.prototype.onSubmit = function() {
		console.log("onSubmit: ");
		var value = this.form.find("textarea").val().trim();
		if ("" === value) {
			console.log("Not posting because nothing to send.");
		} else {
			var self = this;
			var formData = new FormData(self.form[0]);

			// determine public or private
			var encrypted = this.form.find("select[name='encrypt'] option:selected").hasClass("private");
			var entry = this.form.closest(".entry");

			if (entry.length === 1) {
				// if replying to an encrypted entry
				if (!encrypted && entry.hasClass("content-decrypted")) {
					var privateOption = this.form.find("select[name='encrypt'] option.private");
					// force this entry to be encrypted for all mentions
					formData.append("encrypt", "-");
				}
			}

			// copy mentions from enclosed reply
			if (entry.length === 1) {
				formData.append("verb", "reply");
				formData.append("mention", entry.attr("entry"));
				entry.find(".mentions .mention").each(function() {
					var text = $(this).find("span").text();
					if (text.indexOf('@') === 0) {
						text = text.substring(1);
					}
					formData.append("mention", "urn:feed:" + text);
				});
			}

			// find tags and mentions
			var i;
			var match;
			var matches = value.match(/([\@\#]\w*)/g);
			if (matches) {
				for (i in matches) {
					match = matches[i];
					if (match.charAt(0) === '@') {
						match = match.substring(1);
						var feeds = model.findFollowedFeedsMatching(match);
						// if we matched exactly one
						if (feeds.length === 1) {
							// create a mention
							var id = $(feeds[0]).children("id").text();
							if (id) {
								formData.append("mention", id);
							} else {
								console.log("Could not match mention: " + match);
								console.log(feeds[0]);
							}
						}
					} else {
						// otherwise hash->tag
						formData.append("tag", match.substring(1));
					}
				}
			}

			var gruberUrlg = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig;
			matches = value.match(gruberUrlg);
			if (matches) {
				for (i in matches) {
					formData.append("url", matches[i]);
				}
			}

			self.form.addClass("pending");
			self.form.find("button").attr("disabled", "disabled");
			model.updateFeed(formData, function(feedData) {
				self.form.removeClass("pending");
				self.form.find("button").removeAttr("disabled");
				if (feedData) {
					// success
					console.log("updateFeed: result: ");
					console.log(feedData);
					self.form[0].reset();
					self.form.find(".attach").removeAttr("file");
					self.form.removeClass("error");
					controller.forceRender(feedData); // quicker ux
				} else {
					// error
					self.form.addClass("error");
				}
			});
		}
	};

})(window);
<commit_msg>Reply now mentions the originator.<commit_after>/*!
 * Copyright 2014 mpowers
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
(function(window, undefined) {

	/**
	 * Composer manages an entry composer form, submitting its contents to the
	 * model when needed. Variants can just replace or extend this object.
	 */
	Composer = window.Composer = function(form) {
		this.form = $(form);
		var self = this;
		this.form.submit(function(e) {
			e.preventDefault();
			self.onSubmit();
		});
		this.form.find(".submit").click(function(e) {
			e.preventDefault();
			self.form.submit();
		});

		var attach = this.form.find(".attach");
		var input = this.form.find("input[type='file']");
		attach.click(function(e) {
			e.preventDefault();
			input[0].value = ""; // clear any preexisting value
			input.change();
			input.click();
		});
		input.on('change', function(e) {
			// grab file name so the UI can display it
			var value = input[0].value;
			if (value !== undefined) {
				var i = value.lastIndexOf('\\');
				if (i !== -1) {
					value = value.substring(i + 1);
				}
				attach.attr("file", value);
			}
		});
	};
	Composer.prototype = new Composer();
	Composer.prototype.constructor = Composer;

	/**
	 * Adds feed to container, replacing any existing element for this feed.
	 * Returns the added feed element.
	 */
	Composer.prototype.onSubmit = function() {
		console.log("onSubmit: ");
		var value = this.form.find("textarea").val().trim();
		if ("" === value) {
			console.log("Not posting because nothing to send.");
		} else {
			var self = this;
			var formData = new FormData(self.form[0]);

			// determine public or private
			var encrypted = this.form.find("select[name='encrypt'] option:selected").hasClass("private");
			var entry = this.form.closest(".entry");

			if (entry.length === 1) {
				// if replying to an encrypted entry
				if (!encrypted && entry.hasClass("content-decrypted")) {
					var privateOption = this.form.find("select[name='encrypt'] option.private");
					// force this entry to be encrypted for all mentions
					formData.append("encrypt", "-");
				}
			}

			// copy mentions from enclosed reply
			var i;
			if (entry.length === 1) {
				var nodupes = [];
				formData.append("verb", "reply");
				var entryUrn = entry.attr("entry");
				formData.append("mention", entryUrn);
				nodupes.push(model.feedUrnFromFeedId(model.feedIdFromEntryUrn(entryUrn)));
				entry.find(".mentions .mention").each(function() {
					var text = $(this).find("span").text();
					if (text.indexOf('@') === 0) {
						text = text.substring(1);
					}
					text = model.feedUrnFromFeedId(text);
					if (nodupes.indexOf(text) === -1) {
						nodupes.push(text);
					}
				});
				for (i in nodupes) {
					formData.append("mention", nodupes[i]);
				}
			}

			// find tags and mentions
			var match;
			var matches = value.match(/([\@\#]\w*)/g);
			if (matches) {
				for (i in matches) {
					match = matches[i];
					if (match.charAt(0) === '@') {
						match = match.substring(1);
						var feeds = model.findFollowedFeedsMatching(match);
						// if we matched exactly one
						if (feeds.length === 1) {
							// create a mention
							var id = $(feeds[0]).children("id").text();
							if (id) {
								formData.append("mention", id);
							} else {
								console.log("Could not match mention: " + match);
								console.log(feeds[0]);
							}
							// FIXME: until we get our js address checker
						} else if (match.length > 25 && match.length < 35) {
							// assume a mention of that size is an account id
							formData.append("mention", "urn:feed:" + match);
						}
					} else {
						// otherwise hash->tag
						formData.append("tag", match.substring(1));
					}
				}
			}

			var gruberUrlg = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig;
			matches = value.match(gruberUrlg);
			if (matches) {
				for (i in matches) {
					formData.append("url", matches[i]);
				}
			}

			self.form.addClass("pending");
			self.form.find("button").attr("disabled", "disabled");
			model.updateFeed(formData, function(feedData) {
				self.form.removeClass("pending");
				self.form.find("button").removeAttr("disabled");
				if (feedData) {
					// success
					console.log("updateFeed: result: ");
					console.log(feedData);
					self.form[0].reset();
					self.form.find(".attach").removeAttr("file");
					self.form.removeClass("error");
					controller.forceRender(feedData); // quicker ux
				} else {
					// error
					self.form.addClass("error");
				}
			});
		}
	};

})(window);
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import addons from '@storybook/addons'

import Title from './title'
import JSX from './jsx'

const Observable = (channel, api) => {
  return listener => {
    channel.on('kadira/jsx/add_jsx', listener.next('jsx'))
    api.onStory(listener.next('current'))
  }
}

addons.register('kadira/jsx', api => {
  const ob = Observable(addons.getChannel(), api)

  addons.addPanel('kadira/jsx/panel', {
    title: <Title />,
    render: () => <JSX ob={ob} />,
  })
})
<commit_msg>fix(register): pass Title directly to panel<commit_after>import React, { Component } from 'react'
import addons from '@storybook/addons'

import Title from './title'
import JSX from './jsx'

const Observable = (channel, api) => {
  return listener => {
    channel.on('kadira/jsx/add_jsx', listener.next('jsx'))
    api.onStory(listener.next('current'))
  }
}

addons.register('kadira/jsx', api => {
  const ob = Observable(addons.getChannel(), api)

  addons.addPanel('kadira/jsx/panel', {
    title: Title,
    render: () => <JSX ob={ob} />,
  })
})
<|endoftext|>


<commit_before>// cavy-cli reporter object.
export default {

  // Stores the websocket
  ws: null,

  // Internal: Creates a websocket connection to the cavy-cli server.
  connect: function() {
    const url = 'ws://127.0.0.1:8082/';
    this.ws = new WebSocket(url);
  },

  // Internal: Send report to cavy-cli over the websocket connection.
  send: function(report) {
    // WebSocket.readyState 1 means the web socket connection is OPEN.
    if (this.ws.readyState == 1) {
      try {
        this.ws.send(JSON.stringify(report));
        console.log('Cavy test report successfully sent to cavy-cli');
      } catch (e) {
        console.group('Error sending test results');
        console.warn(e.message);
        console.groupEnd();
      }
    } else {
      // If cavy-cli is not running, let people know in a friendly way
      const message = "Skipping sending test report to cavy-cli - if you'd " +
      'like information on how to set up cavy-cli, check out the README ' +
      'https://github.com/pixielabs/cavy-cli';

      console.log(message);
    }
  }
}<commit_msg>remove redundant object attribute<commit_after>// cavy-cli reporter object.
export default {
  // Internal: Creates a websocket connection to the cavy-cli server.
  connect: function() {
    const url = 'ws://127.0.0.1:8082/';
    this.ws = new WebSocket(url);
  },

  // Internal: Send report to cavy-cli over the websocket connection.
  send: function(report) {
    // WebSocket.readyState 1 means the web socket connection is OPEN.
    if (this.ws.readyState == 1) {
      try {
        this.ws.send(JSON.stringify(report));
        console.log('Cavy test report successfully sent to cavy-cli');
      } catch (e) {
        console.group('Error sending test results');
        console.warn(e.message);
        console.groupEnd();
      }
    } else {
      // If cavy-cli is not running, let people know in a friendly way
      const message = "Skipping sending test report to cavy-cli - if you'd " +
      'like information on how to set up cavy-cli, check out the README ' +
      'https://github.com/pixielabs/cavy-cli';

      console.log(message);
    }
  }
}<|endoftext|>


<commit_before>/* @flow */
/* eslint-disable no-use-before-define */

import objectPath from 'object-path';
import util from 'util';
import {
  GraphQLInputObjectType,
  GraphQLNonNull,
  GraphQLEnumType,
  isOutputType,
} from 'graphql';
import TypeMapper from './typeMapper';
import TypeComposer from './typeComposer';
import deepmerge from './utils/deepmerge';
import { only } from './utils/misc';
import { isFunction, isString } from './utils/is';
import { getProjectionFromAST } from './projection';
import type {
  GraphQLArgumentConfig,
  GraphQLFieldConfigArgumentMap,
  GraphQLOutputType,
  ResolverFieldConfig,
  ResolverMWResolve,
  ResolverMWResolveFn,
  ResolveParams,
  ResolverKinds,
  ProjectionType,
  ResolverFilterArgConfig,
  ResolverSortArgConfig,
  ResolverOpts,
  ResolverWrapFn,
  ResolverWrapArgsFn,
  ResolverWrapOutputTypeFn,
  GraphQLInputType,
} from './definition';
import InputTypeComposer from './inputTypeComposer';
import { typeByPath } from './typeByPath';

export default class Resolver {
  outputType: GraphQLOutputType;
  args: GraphQLFieldConfigArgumentMap;
  resolve: ResolverMWResolveFn;
  name: string;
  kind: ?ResolverKinds;
  description: ?string;
  parent: ?Resolver;

  constructor(opts: ResolverOpts = {}) {
    if (!opts.name) {
      throw new Error('For Resolver constructor the `opts.name` is required option.');
    }
    this.name = opts.name;
    this.parent = opts.parent || null;
    this.kind = opts.kind || null;
    this.description = opts.description || '';

    if (opts.outputType) {
      this.setOutputType(opts.outputType);
    }

    if (opts.args) {
      this.args = TypeMapper.convertArgConfigMap(opts.args, this.name, 'Resolver');
    } else {
      this.args = {};
    }

    if (opts.resolve) {
      this.resolve = opts.resolve;
    }
  }

  hasArg(argName: string): boolean {
    return !!this.args[argName];
  }

  getArg(argName: string): ?GraphQLArgumentConfig {
    if (this.hasArg(argName)) {
      return this.args[argName];
    }
    return undefined;
  }

  getArgType(argName: string): GraphQLInputType | void {
    const arg = this.getArg(argName);
    if (arg) {
      return arg.type;
    }
    return undefined;
  }

  getArgs(): GraphQLFieldConfigArgumentMap {
    return this.args;
  }

  setArgs(args: GraphQLFieldConfigArgumentMap): void {
    this.args = TypeMapper.convertArgConfigMap(args, this.name, 'Resolver');
  }

  setArg(argName: string, argConfig: GraphQLArgumentConfig) {
    this.args[argName] = TypeMapper.convertArgConfig(argConfig, argName, this.name, 'Resolver');
  }

  addArgs(newArgs: GraphQLFieldConfigArgumentMap) {
    this.setArgs(Object.assign({}, this.getArgs(), newArgs));
  }

  removeArg(argName: string) {
    delete this.args[argName];
  }

  isRequired(argName: string): boolean {
    return this.getArgType(argName) instanceof GraphQLNonNull;
  }

  makeRequired(argNameOrArray: string | Array<string>) {
    const argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
    const args = this.getArgs();
    argNames.forEach((argName) => {
      if (args[argName]) {
        if (!(args[argName].type instanceof GraphQLNonNull)) {
          args[argName].type = new GraphQLNonNull(args[argName].type);
        }
      }
    });
    this.setArgs(args);
  }

  makeOptional(argNameOrArray: string | Array<string>) {
    const argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
    const args = this.getArgs();
    argNames.forEach((argName) => {
      if (argNames.includes(argName)) {
        if (args[argName].type instanceof GraphQLNonNull) {
          args[argName].type = args[argName].type.ofType;
        }
      }
    });
    this.setArgs(args);
  }

  /*
  * This method should be overriden via constructor
  */
  resolve(resolveParams: ResolveParams): Promise<any> { // eslint-disable-line
    return Promise.resolve();
  }

  getResolve():ResolverMWResolveFn {
    return this.resolve;
  }

  setResolve(resolve: ResolverMWResolveFn): void {
    this.resolve = resolve;
  }

  getOutputType(): GraphQLOutputType {
    return this.outputType;
  }

  setOutputType(gqType: GraphQLOutputType | string) {
    let type;

    if (gqType instanceof TypeComposer) {
      this.outputType = gqType.getType();
      return;
    }

    if (isString(gqType)) {
      // $FlowFixMe
      if (gqType.indexOf('{') === -1) {
        // $FlowFixMe
        type = TypeMapper.getWrapped(gqType);
      } else {
        // $FlowFixMe
        type = TypeMapper.createType(gqType);
      }
    } else {
      type = gqType;
    }

    // $FlowFixMe
    if (!isOutputType(type)) {
      throw new Error('You should provide correct OutputType for Resolver.outputType.');
    }
    // $FlowFixMe
    this.outputType = type;
  }

  getFieldConfig(
    opts: {
      projection?: ProjectionType,
    } = {}
  ): ResolverFieldConfig {
    const resolve = this.getResolve();
    return {
      type: this.getOutputType(),
      args: this.getArgs(),
      resolve: (source, args, context, info) => {
        let projection = getProjectionFromAST(info);
        if (opts.projection) {
          projection = deepmerge(projection, opts.projection);
        }
        return resolve({ source, args, context, info, projection });
      },
    };
  }

  getKind(): ?ResolverKinds {
    return this.kind;
  }

  setKind(kind: string) {
    if (kind !== 'query' && kind !== 'mutation' && kind !== 'subscription') {
      throw new Error(`You provide incorrect value '${kind}' for Resolver.setKind method. `
                    + 'Valid values are: query | mutation | subscription');
    }
    this.kind = kind;
  }

  getDescription(): ?string {
    return this.description;
  }

  setDescription(description: string) {
    this.description = description;
  }

  get(path: string | Array<string>): mixed {
    return typeByPath(this, path);
  }

  clone(opts: ResolverOpts = {}): Resolver {
    const oldOpts = {};
    for (const key in this) { // eslint-disable-line no-restricted-syntax
      if ({}.hasOwnProperty.call(this, key)) {
        // $FlowFixMe
        oldOpts[key] = this[key];
      }
    }
    oldOpts.args = Object.assign({}, this.args);
    return new Resolver(Object.assign({}, oldOpts, opts));
  }

  wrap(cb: ?ResolverWrapFn, opts: ?ResolverOpts = {}): Resolver {
    const prevResolver = this;
    const newResolver = this.clone({
      name: 'wrap',
      parent: prevResolver,
      ...opts,
    });

    if (isFunction(cb)) {
      // $FlowFixMe
      cb(newResolver, prevResolver);
    }

    return newResolver;
  }

  wrapResolve(cb: ResolverMWResolve, wrapperName: string = 'wrapResolve'): Resolver {
    return this.wrap(
      (newResolver, prevResolver) => {
        const newResolve = cb(prevResolver.getResolve());
        newResolver.setResolve(newResolve);
        return newResolver;
      },
      { name: wrapperName }
    );
  }

  wrapArgs(cb: ResolverWrapArgsFn, wrapperName: string = 'wrapArgs'): Resolver {
    return this.wrap(
      (newResolver, prevResolver) => {
        // clone prevArgs, to avoid changing args in callback
        const prevArgs = Object.assign({}, prevResolver.getArgs());
        const newArgs = cb(prevArgs);
        newResolver.setArgs(newArgs);
        return newResolver;
      },
      { name: wrapperName }
    );
  }

  wrapOutputType(cb: ResolverWrapOutputTypeFn, wrapperName: string = 'wrapOutputType'): Resolver {
    return this.wrap(
      (newResolver, prevResolver) => {
        const prevOutputType = prevResolver.getOutputType();
        const newOutputType = cb(prevOutputType);
        newResolver.setOutputType(newOutputType);
        return newResolver;
      },
      { name: wrapperName }
    );
  }

  addFilterArg(opts: ResolverFilterArgConfig): Resolver {
    if (!opts.name) {
      throw new Error('For Resolver.addFilterArg the arg name `opts.name` is required.');
    }

    if (!opts.type) {
      throw new Error('For Resolver.addFilterArg the arg type `opts.type` is required.');
    }

    const resolver = this.wrap(null, { name: 'addFilterArg' });

    // get filterTC or create new one argument
    const filter = resolver.getArg('filter');
    let filterITC;
    if (filter && filter.type instanceof GraphQLInputObjectType) {
      filterITC = new InputTypeComposer(filter.type);
    } else {
      if (!opts.filterTypeNameFallback || !isString(opts.filterTypeNameFallback)) {
        throw new Error('For Resolver.addFilterArg needs to provide `opts.filterTypeNameFallback: string`. '
                      + 'This string will be used as unique name for `filter` type of input argument. '
                      + 'Eg. FilterXXXXXInput');
      }
      filterITC = InputTypeComposer.create(opts.filterTypeNameFallback);
    }

    let defaultValue;
    if (filter && filter.defaultValue) {
      defaultValue = filter.defaultValue;
    }
    if (opts.defaultValue) {
      if (!defaultValue) {
        defaultValue = {};
      }
      // $FlowFixMe
      defaultValue[opts.name] = opts.defaultValue;
    }

    resolver.setArg('filter', {
      type: filterITC.getType(),
      description: (filter && filter.description) || undefined,
      defaultValue,
    });

    filterITC.setField(opts.name, {
      ...only(opts, ['name', 'type', 'defaultValue', 'description']),
    });

    const resolveNext = resolver.getResolve();
    if (isFunction(opts.query)) {
      resolver.setResolve((resolveParams: ResolveParams) => {
        const value = objectPath.get(resolveParams, ['args', 'filter', opts.name]);
        if (value) {
          if (!resolveParams.rawQuery) {
            resolveParams.rawQuery = {}; // eslint-disable-line
          }
          opts.query(resolveParams.rawQuery, value, resolveParams);
        }
        return resolveNext(resolveParams);
      });
    }

    return resolver;
  }

  addSortArg(opts: ResolverSortArgConfig): Resolver {
    if (!opts.name) {
      throw new Error('For Resolver.addSortArg the `opts.name` is required.');
    }

    if (!opts.value) {
      throw new Error('For Resolver.addSortArg the `opts.value` is required.');
    }

    const resolver = this.wrap(null, { name: 'addSortArg' });

    // get sortEnumType or create new one
    const sort = resolver.getArg('sort');
    let sortEnumType;
    if (sort) {
      if (sort.type instanceof GraphQLEnumType) {
        sortEnumType = sort.type;
      } else {
        throw new Error('Resolver should have `sort` arg with type GraphQLEnumType. '
                      + `But got: ${util.inspect(sort.type, { depth: 2 })} `);
      }
    } else {
      if (!opts.sortTypeNameFallback || !isString(opts.sortTypeNameFallback)) {
        throw new Error('For Resolver.addSortArg needs to provide `opts.sortTypeNameFallback: string`. '
                      + 'This string will be used as unique name for `sort` type of input argument. '
                      + 'Eg. SortXXXXXEnum');
      }
      sortEnumType = new GraphQLEnumType({
        name: opts.sortTypeNameFallback,
        values: {
          [opts.name]: {},
        },
      });
      resolver.setArg('sort', { type: sortEnumType });
    }

    // extend sortEnumType with new sorting value
    const existedIdx = sortEnumType._values.findIndex(o => o.name === opts.name);
    if (existedIdx >= 0) {
      sortEnumType._values.splice(existedIdx, 1);
    }
    delete sortEnumType._nameLookup;
    delete sortEnumType._valueLookup;
    sortEnumType._values.push({
      name: opts.name,
      description: opts.description,
      isDeprecated: Boolean(opts.deprecationReason),
      deprecationReason: opts.deprecationReason,
      value: isFunction(opts.value) ? opts.name : opts.value,
    });

    // If sort value is evaluable (function), then wrap resolve method
    const resolveNext = resolver.getResolve();
    if (isFunction(opts.value)) {
      resolver.setResolve((resolveParams: ResolveParams) => {
        const value = objectPath.get(resolveParams, ['args', 'sort']);
        if (value === opts.name) {
          // $FlowFixMe
          const newSortValue = opts.value(resolveParams);
          resolveParams.args.sort = newSortValue; // eslint-disable-line
        }
        return resolveNext(resolveParams);
      });
    }

    return resolver;
  }

  getNestedName() {
    if (this.parent) {
      return `${this.name}(${this.parent.getNestedName()})`;
    }
    return this.name;
  }

  toString() {
    function extendedInfo(resolver, spaces = '') {
      return [
        'Resolver(',
        `  name: ${resolver.name},`,
        `  outputType: ${util.inspect(resolver.outputType, { depth: 2 })},`,
        `  args: ${util.inspect(resolver.args, { depth: 3 }).replace('\n', `\n  ${spaces}`)},`,
        `  resolve: ${resolver.resolve ? resolver.resolve.toString().replace('\n', `\n  ${spaces}`) : 'undefined'},`,
        `  parent: ${resolver.parent ? extendedInfo(resolver.parent, `  ${spaces}`) : ''}`,
        ')',
      ].filter(s => !!s).join(`\n  ${spaces}`);
    }

    return extendedInfo(this);
  }
}
<commit_msg>fix(Resolver): Pass resolver description to the fieldConfig<commit_after>/* @flow */
/* eslint-disable no-use-before-define */

import objectPath from 'object-path';
import util from 'util';
import {
  GraphQLInputObjectType,
  GraphQLNonNull,
  GraphQLEnumType,
  isOutputType,
} from 'graphql';
import TypeMapper from './typeMapper';
import TypeComposer from './typeComposer';
import deepmerge from './utils/deepmerge';
import { only } from './utils/misc';
import { isFunction, isString } from './utils/is';
import { getProjectionFromAST } from './projection';
import type {
  GraphQLArgumentConfig,
  GraphQLFieldConfigArgumentMap,
  GraphQLOutputType,
  ResolverFieldConfig,
  ResolverMWResolve,
  ResolverMWResolveFn,
  ResolveParams,
  ResolverKinds,
  ProjectionType,
  ResolverFilterArgConfig,
  ResolverSortArgConfig,
  ResolverOpts,
  ResolverWrapFn,
  ResolverWrapArgsFn,
  ResolverWrapOutputTypeFn,
  GraphQLInputType,
} from './definition';
import InputTypeComposer from './inputTypeComposer';
import { typeByPath } from './typeByPath';

export default class Resolver {
  outputType: GraphQLOutputType;
  args: GraphQLFieldConfigArgumentMap;
  resolve: ResolverMWResolveFn;
  name: string;
  kind: ?ResolverKinds;
  description: ?string;
  parent: ?Resolver;

  constructor(opts: ResolverOpts = {}) {
    if (!opts.name) {
      throw new Error('For Resolver constructor the `opts.name` is required option.');
    }
    this.name = opts.name;
    this.parent = opts.parent || null;
    this.kind = opts.kind || null;
    this.description = opts.description || '';

    if (opts.outputType) {
      this.setOutputType(opts.outputType);
    }

    if (opts.args) {
      this.args = TypeMapper.convertArgConfigMap(opts.args, this.name, 'Resolver');
    } else {
      this.args = {};
    }

    if (opts.resolve) {
      this.resolve = opts.resolve;
    }
  }

  hasArg(argName: string): boolean {
    return !!this.args[argName];
  }

  getArg(argName: string): ?GraphQLArgumentConfig {
    if (this.hasArg(argName)) {
      return this.args[argName];
    }
    return undefined;
  }

  getArgType(argName: string): GraphQLInputType | void {
    const arg = this.getArg(argName);
    if (arg) {
      return arg.type;
    }
    return undefined;
  }

  getArgs(): GraphQLFieldConfigArgumentMap {
    return this.args;
  }

  setArgs(args: GraphQLFieldConfigArgumentMap): void {
    this.args = TypeMapper.convertArgConfigMap(args, this.name, 'Resolver');
  }

  setArg(argName: string, argConfig: GraphQLArgumentConfig) {
    this.args[argName] = TypeMapper.convertArgConfig(argConfig, argName, this.name, 'Resolver');
  }

  addArgs(newArgs: GraphQLFieldConfigArgumentMap) {
    this.setArgs(Object.assign({}, this.getArgs(), newArgs));
  }

  removeArg(argName: string) {
    delete this.args[argName];
  }

  isRequired(argName: string): boolean {
    return this.getArgType(argName) instanceof GraphQLNonNull;
  }

  makeRequired(argNameOrArray: string | Array<string>) {
    const argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
    const args = this.getArgs();
    argNames.forEach((argName) => {
      if (args[argName]) {
        if (!(args[argName].type instanceof GraphQLNonNull)) {
          args[argName].type = new GraphQLNonNull(args[argName].type);
        }
      }
    });
    this.setArgs(args);
  }

  makeOptional(argNameOrArray: string | Array<string>) {
    const argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
    const args = this.getArgs();
    argNames.forEach((argName) => {
      if (argNames.includes(argName)) {
        if (args[argName].type instanceof GraphQLNonNull) {
          args[argName].type = args[argName].type.ofType;
        }
      }
    });
    this.setArgs(args);
  }

  /*
  * This method should be overriden via constructor
  */
  resolve(resolveParams: ResolveParams): Promise<any> { // eslint-disable-line
    return Promise.resolve();
  }

  getResolve():ResolverMWResolveFn {
    return this.resolve;
  }

  setResolve(resolve: ResolverMWResolveFn): void {
    this.resolve = resolve;
  }

  getOutputType(): GraphQLOutputType {
    return this.outputType;
  }

  setOutputType(gqType: GraphQLOutputType | string) {
    let type;

    if (gqType instanceof TypeComposer) {
      this.outputType = gqType.getType();
      return;
    }

    if (isString(gqType)) {
      // $FlowFixMe
      if (gqType.indexOf('{') === -1) {
        // $FlowFixMe
        type = TypeMapper.getWrapped(gqType);
      } else {
        // $FlowFixMe
        type = TypeMapper.createType(gqType);
      }
    } else {
      type = gqType;
    }

    // $FlowFixMe
    if (!isOutputType(type)) {
      throw new Error('You should provide correct OutputType for Resolver.outputType.');
    }
    // $FlowFixMe
    this.outputType = type;
  }

  getFieldConfig(
    opts: {
      projection?: ProjectionType,
    } = {}
  ): ResolverFieldConfig {
    const resolve = this.getResolve();
    return {
      type: this.getOutputType(),
      args: this.getArgs(),
      description: this.description,
      resolve: (source, args, context, info) => {
        let projection = getProjectionFromAST(info);
        if (opts.projection) {
          projection = deepmerge(projection, opts.projection);
        }
        return resolve({ source, args, context, info, projection });
      },
    };
  }

  getKind(): ?ResolverKinds {
    return this.kind;
  }

  setKind(kind: string) {
    if (kind !== 'query' && kind !== 'mutation' && kind !== 'subscription') {
      throw new Error(`You provide incorrect value '${kind}' for Resolver.setKind method. `
                    + 'Valid values are: query | mutation | subscription');
    }
    this.kind = kind;
  }

  getDescription(): ?string {
    return this.description;
  }

  setDescription(description: string) {
    this.description = description;
  }

  get(path: string | Array<string>): mixed {
    return typeByPath(this, path);
  }

  clone(opts: ResolverOpts = {}): Resolver {
    const oldOpts = {};
    for (const key in this) { // eslint-disable-line no-restricted-syntax
      if ({}.hasOwnProperty.call(this, key)) {
        // $FlowFixMe
        oldOpts[key] = this[key];
      }
    }
    oldOpts.args = Object.assign({}, this.args);
    return new Resolver(Object.assign({}, oldOpts, opts));
  }

  wrap(cb: ?ResolverWrapFn, opts: ?ResolverOpts = {}): Resolver {
    const prevResolver = this;
    const newResolver = this.clone({
      name: 'wrap',
      parent: prevResolver,
      ...opts,
    });

    if (isFunction(cb)) {
      // $FlowFixMe
      cb(newResolver, prevResolver);
    }

    return newResolver;
  }

  wrapResolve(cb: ResolverMWResolve, wrapperName: string = 'wrapResolve'): Resolver {
    return this.wrap(
      (newResolver, prevResolver) => {
        const newResolve = cb(prevResolver.getResolve());
        newResolver.setResolve(newResolve);
        return newResolver;
      },
      { name: wrapperName }
    );
  }

  wrapArgs(cb: ResolverWrapArgsFn, wrapperName: string = 'wrapArgs'): Resolver {
    return this.wrap(
      (newResolver, prevResolver) => {
        // clone prevArgs, to avoid changing args in callback
        const prevArgs = Object.assign({}, prevResolver.getArgs());
        const newArgs = cb(prevArgs);
        newResolver.setArgs(newArgs);
        return newResolver;
      },
      { name: wrapperName }
    );
  }

  wrapOutputType(cb: ResolverWrapOutputTypeFn, wrapperName: string = 'wrapOutputType'): Resolver {
    return this.wrap(
      (newResolver, prevResolver) => {
        const prevOutputType = prevResolver.getOutputType();
        const newOutputType = cb(prevOutputType);
        newResolver.setOutputType(newOutputType);
        return newResolver;
      },
      { name: wrapperName }
    );
  }

  addFilterArg(opts: ResolverFilterArgConfig): Resolver {
    if (!opts.name) {
      throw new Error('For Resolver.addFilterArg the arg name `opts.name` is required.');
    }

    if (!opts.type) {
      throw new Error('For Resolver.addFilterArg the arg type `opts.type` is required.');
    }

    const resolver = this.wrap(null, { name: 'addFilterArg' });

    // get filterTC or create new one argument
    const filter = resolver.getArg('filter');
    let filterITC;
    if (filter && filter.type instanceof GraphQLInputObjectType) {
      filterITC = new InputTypeComposer(filter.type);
    } else {
      if (!opts.filterTypeNameFallback || !isString(opts.filterTypeNameFallback)) {
        throw new Error('For Resolver.addFilterArg needs to provide `opts.filterTypeNameFallback: string`. '
                      + 'This string will be used as unique name for `filter` type of input argument. '
                      + 'Eg. FilterXXXXXInput');
      }
      filterITC = InputTypeComposer.create(opts.filterTypeNameFallback);
    }

    let defaultValue;
    if (filter && filter.defaultValue) {
      defaultValue = filter.defaultValue;
    }
    if (opts.defaultValue) {
      if (!defaultValue) {
        defaultValue = {};
      }
      // $FlowFixMe
      defaultValue[opts.name] = opts.defaultValue;
    }

    resolver.setArg('filter', {
      type: filterITC.getType(),
      description: (filter && filter.description) || undefined,
      defaultValue,
    });

    filterITC.setField(opts.name, {
      ...only(opts, ['name', 'type', 'defaultValue', 'description']),
    });

    const resolveNext = resolver.getResolve();
    if (isFunction(opts.query)) {
      resolver.setResolve((resolveParams: ResolveParams) => {
        const value = objectPath.get(resolveParams, ['args', 'filter', opts.name]);
        if (value) {
          if (!resolveParams.rawQuery) {
            resolveParams.rawQuery = {}; // eslint-disable-line
          }
          opts.query(resolveParams.rawQuery, value, resolveParams);
        }
        return resolveNext(resolveParams);
      });
    }

    return resolver;
  }

  addSortArg(opts: ResolverSortArgConfig): Resolver {
    if (!opts.name) {
      throw new Error('For Resolver.addSortArg the `opts.name` is required.');
    }

    if (!opts.value) {
      throw new Error('For Resolver.addSortArg the `opts.value` is required.');
    }

    const resolver = this.wrap(null, { name: 'addSortArg' });

    // get sortEnumType or create new one
    const sort = resolver.getArg('sort');
    let sortEnumType;
    if (sort) {
      if (sort.type instanceof GraphQLEnumType) {
        sortEnumType = sort.type;
      } else {
        throw new Error('Resolver should have `sort` arg with type GraphQLEnumType. '
                      + `But got: ${util.inspect(sort.type, { depth: 2 })} `);
      }
    } else {
      if (!opts.sortTypeNameFallback || !isString(opts.sortTypeNameFallback)) {
        throw new Error('For Resolver.addSortArg needs to provide `opts.sortTypeNameFallback: string`. '
                      + 'This string will be used as unique name for `sort` type of input argument. '
                      + 'Eg. SortXXXXXEnum');
      }
      sortEnumType = new GraphQLEnumType({
        name: opts.sortTypeNameFallback,
        values: {
          [opts.name]: {},
        },
      });
      resolver.setArg('sort', { type: sortEnumType });
    }

    // extend sortEnumType with new sorting value
    const existedIdx = sortEnumType._values.findIndex(o => o.name === opts.name);
    if (existedIdx >= 0) {
      sortEnumType._values.splice(existedIdx, 1);
    }
    delete sortEnumType._nameLookup;
    delete sortEnumType._valueLookup;
    sortEnumType._values.push({
      name: opts.name,
      description: opts.description,
      isDeprecated: Boolean(opts.deprecationReason),
      deprecationReason: opts.deprecationReason,
      value: isFunction(opts.value) ? opts.name : opts.value,
    });

    // If sort value is evaluable (function), then wrap resolve method
    const resolveNext = resolver.getResolve();
    if (isFunction(opts.value)) {
      resolver.setResolve((resolveParams: ResolveParams) => {
        const value = objectPath.get(resolveParams, ['args', 'sort']);
        if (value === opts.name) {
          // $FlowFixMe
          const newSortValue = opts.value(resolveParams);
          resolveParams.args.sort = newSortValue; // eslint-disable-line
        }
        return resolveNext(resolveParams);
      });
    }

    return resolver;
  }

  getNestedName() {
    if (this.parent) {
      return `${this.name}(${this.parent.getNestedName()})`;
    }
    return this.name;
  }

  toString() {
    function extendedInfo(resolver, spaces = '') {
      return [
        'Resolver(',
        `  name: ${resolver.name},`,
        `  outputType: ${util.inspect(resolver.outputType, { depth: 2 })},`,
        `  args: ${util.inspect(resolver.args, { depth: 3 }).replace('\n', `\n  ${spaces}`)},`,
        `  resolve: ${resolver.resolve ? resolver.resolve.toString().replace('\n', `\n  ${spaces}`) : 'undefined'},`,
        `  parent: ${resolver.parent ? extendedInfo(resolver.parent, `  ${spaces}`) : ''}`,
        ')',
      ].filter(s => !!s).join(`\n  ${spaces}`);
    }

    return extendedInfo(this);
  }
}
<|endoftext|>


<commit_before>export default class Configurator {
  constructor(config) {
    this.config = config;
  }

  configure() {}

  get app() {
    return this.config.app;
  }

  get logger() {
    return this.config.logger;
  }

  log(msg) {
    this.logger.log(msg);
  }
}
<commit_msg>add base validation<commit_after>import util from 'util';

export default class Configurator {
  constructor(config = {}) {
    this.config = config;
    this.validate();
  }

  validate() {
    if (typeof this.config !== 'object') {
      throw `config must be an object, was: ${util.inspect(this.config)}`;
    }
  }

  configure() {}

  get app() {
    return this.config.app;
  }

  get logger() {
    return this.config.logger;
  }

  log(msg) {
    this.logger.log(msg);
  }
}
<|endoftext|>


<commit_before>'use strict'

const util = require('./util')
const cidForHash = require('./common').cidForHash

exports = module.exports

exports.multicodec = 'eth-block'

/*
 * resolve: receives a path and a block and returns the value on path,
 * throw if not possible. `block` is an IPFS Block instance (contains data + key)
 */
exports.resolve = (block, path, callback) => {
  util.deserialize(block.data, (err, ethBlock) => {
    if (err) return callback(err)
    exports.resolveFromObject(ethBlock, path, callback)
  })
}

exports.resolveFromObject = (ethBlock, path, callback) => {
  let result

  // root
  if (!path || path === '/') {
    result = { value: ethBlock, remainderPath: '' }
    return callback(null, result)
  }

  // check tree results
  let pathParts = path.split('/')
  let firstPart = pathParts.shift()
  let remainderPath = pathParts.join('/')

  exports.treeFromObject(ethBlock, {}, (err, paths) => {
    if (err) return callback(err)
    let treeResult = paths.find(child => child.path === firstPart)
    if (!treeResult) {
      let err = new Error('Path not found ("' + firstPart + '").')
      return callback(err)
    }

    result = {
      value: treeResult.value,
      remainderPath: remainderPath
    }
    return callback(null, result)
  })
}

/*
 * tree: returns a flattened array with paths: values of the project. options
 * are option (i.e. nestness)
 */

exports.tree = (block, options, callback) => {
  // parse arguments
  if (typeof options === 'function') {
    callback = options
    options = undefined
  }
  if (!options) {
    options = {}
  }

  util.deserialize(block.data, (err, ethBlock) => {
    if (err) return callback(err)
    exports.treeFromObject(ethBlock, options, callback)
  })
}

exports.treeFromObject = (ethBlock, options, callback) => {
  const paths = []

  // external links
  paths.push({
    path: 'parent',
    value: { '/': cidForHash('eth-block', ethBlock.parentHash).toBaseEncodedString() }
  })
  paths.push({
    path: 'ommers',
    value: { '/': cidForHash('eth-block-list', ethBlock.uncleHash).toBaseEncodedString() }
  })
  paths.push({
    path: 'transactions',
    value: { '/': cidForHash('eth-tx-trie', ethBlock.transactionsTrie).toBaseEncodedString() }
  })
  paths.push({
    path: 'transactionReceipts',
    value: { '/': cidForHash('eth-tx-receipt-trie', ethBlock.receiptTrie).toBaseEncodedString() }
  })
  paths.push({
    path: 'state',
    value: { '/': cidForHash('eth-state-trie', ethBlock.stateRoot).toBaseEncodedString() }
  })

  // external links as data
  paths.push({
    path: 'parentHash',
    value: ethBlock.parentHash
  })
  paths.push({
    path: 'ommerHash',
    value: ethBlock.uncleHash
  })
  paths.push({
    path: 'transactionTrieRoot',
    value: ethBlock.transactionsTrie
  })
  paths.push({
    path: 'transactionReceiptTrieRoot',
    value: ethBlock.receiptTrie
  })
  paths.push({
    path: 'stateRoot',
    value: ethBlock.stateRoot
  })

  // internal data
  paths.push({
    path: 'authorAddress',
    value: ethBlock.coinbase
  })
  paths.push({
    path: 'bloom',
    value: ethBlock.bloom
  })
  paths.push({
    path: 'difficulty',
    value: ethBlock.difficulty
  })
  paths.push({
    path: 'number',
    value: ethBlock.number
  })
  paths.push({
    path: 'gasLimit',
    value: ethBlock.gasLimit
  })
  paths.push({
    path: 'gasUsed',
    value: ethBlock.gasUsed
  })
  paths.push({
    path: 'timestamp',
    value: ethBlock.timestamp
  })
  paths.push({
    path: 'extraData',
    value: ethBlock.extraData
  })
  paths.push({
    path: 'mixHash',
    value: ethBlock.mixHash
  })
  paths.push({
    path: 'nonce',
    value: ethBlock.nonce
  })

  callback(null, paths)
}

exports.isLink = (block, path, callback) => {
  exports.resolve(block, path, (err, result) => {
    if (err) {
      return callback(err)
    }

    if (result.remainderPath.length > 0) {
      return callback(new Error('path out of scope'))
    }

    if (typeof result.value === 'object' && result.value['/']) {
      callback(null, result.value)
    } else {
      callback(null, false)
    }
  })
}<commit_msg>lint fix<commit_after>'use strict'

const util = require('./util')
const cidForHash = require('./common').cidForHash

exports = module.exports

exports.multicodec = 'eth-block'

/*
 * resolve: receives a path and a block and returns the value on path,
 * throw if not possible. `block` is an IPFS Block instance (contains data + key)
 */
exports.resolve = (block, path, callback) => {
  util.deserialize(block.data, (err, ethBlock) => {
    if (err) return callback(err)
    exports.resolveFromObject(ethBlock, path, callback)
  })
}

exports.resolveFromObject = (ethBlock, path, callback) => {
  let result

  // root
  if (!path || path === '/') {
    result = { value: ethBlock, remainderPath: '' }
    return callback(null, result)
  }

  // check tree results
  let pathParts = path.split('/')
  let firstPart = pathParts.shift()
  let remainderPath = pathParts.join('/')

  exports.treeFromObject(ethBlock, {}, (err, paths) => {
    if (err) return callback(err)
    let treeResult = paths.find(child => child.path === firstPart)
    if (!treeResult) {
      let err = new Error('Path not found ("' + firstPart + '").')
      return callback(err)
    }

    result = {
      value: treeResult.value,
      remainderPath: remainderPath
    }
    return callback(null, result)
  })
}

/*
 * tree: returns a flattened array with paths: values of the project. options
 * are option (i.e. nestness)
 */

exports.tree = (block, options, callback) => {
  // parse arguments
  if (typeof options === 'function') {
    callback = options
    options = undefined
  }
  if (!options) {
    options = {}
  }

  util.deserialize(block.data, (err, ethBlock) => {
    if (err) return callback(err)
    exports.treeFromObject(ethBlock, options, callback)
  })
}

exports.treeFromObject = (ethBlock, options, callback) => {
  const paths = []

  // external links
  paths.push({
    path: 'parent',
    value: { '/': cidForHash('eth-block', ethBlock.parentHash).toBaseEncodedString() }
  })
  paths.push({
    path: 'ommers',
    value: { '/': cidForHash('eth-block-list', ethBlock.uncleHash).toBaseEncodedString() }
  })
  paths.push({
    path: 'transactions',
    value: { '/': cidForHash('eth-tx-trie', ethBlock.transactionsTrie).toBaseEncodedString() }
  })
  paths.push({
    path: 'transactionReceipts',
    value: { '/': cidForHash('eth-tx-receipt-trie', ethBlock.receiptTrie).toBaseEncodedString() }
  })
  paths.push({
    path: 'state',
    value: { '/': cidForHash('eth-state-trie', ethBlock.stateRoot).toBaseEncodedString() }
  })

  // external links as data
  paths.push({
    path: 'parentHash',
    value: ethBlock.parentHash
  })
  paths.push({
    path: 'ommerHash',
    value: ethBlock.uncleHash
  })
  paths.push({
    path: 'transactionTrieRoot',
    value: ethBlock.transactionsTrie
  })
  paths.push({
    path: 'transactionReceiptTrieRoot',
    value: ethBlock.receiptTrie
  })
  paths.push({
    path: 'stateRoot',
    value: ethBlock.stateRoot
  })

  // internal data
  paths.push({
    path: 'authorAddress',
    value: ethBlock.coinbase
  })
  paths.push({
    path: 'bloom',
    value: ethBlock.bloom
  })
  paths.push({
    path: 'difficulty',
    value: ethBlock.difficulty
  })
  paths.push({
    path: 'number',
    value: ethBlock.number
  })
  paths.push({
    path: 'gasLimit',
    value: ethBlock.gasLimit
  })
  paths.push({
    path: 'gasUsed',
    value: ethBlock.gasUsed
  })
  paths.push({
    path: 'timestamp',
    value: ethBlock.timestamp
  })
  paths.push({
    path: 'extraData',
    value: ethBlock.extraData
  })
  paths.push({
    path: 'mixHash',
    value: ethBlock.mixHash
  })
  paths.push({
    path: 'nonce',
    value: ethBlock.nonce
  })

  callback(null, paths)
}

exports.isLink = (block, path, callback) => {
  exports.resolve(block, path, (err, result) => {
    if (err) {
      return callback(err)
    }

    if (result.remainderPath.length > 0) {
      return callback(new Error('path out of scope'))
    }

    if (typeof result.value === 'object' && result.value['/']) {
      callback(null, result.value)
    } else {
      callback(null, false)
    }
  })
}
<|endoftext|>