<commit_before>/*! Angulocity 0.1.0. (C) 2015 John Cui. MIT @license: en.wikipedia.org/wiki/MIT_License */
/*jshint bitwise: false*/
;(function (window, undefined) {
  'use strict';

  /* Create some shortcut references to the libraries */
  var a = window.angular,
      v = window.Velocity || window.jQuery.Velocity,
      $ = $ ? $ : window.jQuery;

  /* Generate some error messages in the console if Velocity or Velocity UI is not found */
  if (!v) {
    console.error('Angulocity: Velocity animation engine is not found. Animations are disabled.');
    v = a.noop;
  } else if (!v.RegisterEffect) {
    console.error('Angulocity: Velocity UI Pack is not found. Additional animation effects will not be available.');
  }

  /* Detect IE and add appropriate angular ie shims */
  if (/MSIE/.test(window.navigator.userAgent)) {
    window.document.createElement('ngv-element');
    window.document.createElement('ngv-collection');
    window.document.createElement('ngv-animator');
  }

  /**
   * Setup Velocity options based on attributes passed to the
   * current element.
   *
   * @param   mixed   scope   Scope used to evaluate angular attribure expressions
   * @param   mixed   iAttrs  Attribute list
   * @param   bool    bIn     True if animating "In", false if animating "Out"
   *
   * @return  mixed           Velocity options object
   *
   */
  function getNgvOptions (scope, iAttrs, bIn) {
    var options = {
      /* Velocity's default options */
      duration: iAttrs.ngvDuration ? iAttrs.ngvDuration : 400,
      delay: iAttrs.ngvDelay ? scope.$eval(iAttrs.ngvDelay) : false,
      easing: iAttrs.ngvEasing ? iAttrs.ngvEasing : 'easeInOutQuart',
      queue: iAttrs.ngvQueue ? iAttrs.ngvQueue : undefined,
      begin: iAttrs.ngvBegin ? scope.$eval(iAttrs.ngvBegin) : undefined,
      progress: iAttrs.ngvProgress ? scope.$eval(iAttrs.ngvProgress) : undefined,
      complete: iAttrs.ngvComplete ? scope.$eval(iAttrs.ngvComplete) : undefined,
      display: iAttrs.ngvDisplay ? iAttrs.ngvDisplay : undefined,
      visibility: iAttrs.ngvVisibility ? iAttrs.ngvVisibility : undefined,
      loop: iAttrs.ngvLoop ? scope.$eval(iAttrs.ngvLoop) : undefined,
      stagger: iAttrs.ngvStagger ? scope.$eval(iAttrs.ngvStagger) : undefined,
      drag: iAttrs.ngvDrag ? scope.$eval(iAttrs.ngvDrag) : undefined,
      mobileHA: iAttrs.ngvMobileHa ? scope.$eval(iAttrs.ngvMobileHa) :  true
    };

    /* Alter some options based on whether animating "In" or "Out" */
    if (bIn === true) {
      options.display = iAttrs.ngvInDisplay ? iAttrs.ngvInDisplay : options.display;
      options.visibility = iAttrs.ngvInVisibility ? iAttrs.ngvInVisibility : options.visibility;
      options.duration = iAttrs.ngvInDuration ? iAttrs.ngvInDuration : options.duration;
      options.delay = iAttrs.ngvInDelay ? iAttrs.ngvInDelay : options.delay;
    } else if (bIn === false) {
      options.display = iAttrs.ngvOutDisplay ? iAttrs.ngvOutDisplay : options.display;
      options.visibility = iAttrs.ngvOutVisibility ? iAttrs.ngvOutVisibility : options.visibility;
      options.duration = iAttrs.ngvOutDuration ? iAttrs.ngvOutDuration : options.duration;
      options.delay = iAttrs.ngvOutDelay ? iAttrs.ngvOutDelay : options.delay;
    }

    return options;
  }

  /**
   * Utility function to generate hash
   *
   * @param   string  s       String to perform has on
   *
   * @return  string          Hashed value of string
   */
  function getNgvHash(s) {
    var i = 0, x = 0, c;
    for (; i < s.length; i++) {
      c = s.charCodeAt(i);
      x = ((x<<5)-x)+c;
    }
    x |= 0;
    return x.toString(16);
  }
  /**
   * Utility function to generate hash for class names.
   *
   * @param   string  klass   Whitespace separated class values
   *
   * @return  string          Hashed class values
   *
   */
  function getNgvClass(klass) {
    var i;
    klass = klass.trim().split(/\s/);
    for (i = 0; i < klass.length; i++) {
      klass[i] = 'ngv' + getNgvHash('ngv' + klass[i].toLowerCase());
    }
    return klass.join(' ');
  }

  /**
   * Get collection of nodes within an element via selector
   *
   * @param   element iElement  Root element node containing the collection
   * @param   string  selector  Query selector for finding child elements
   *
   * @return  array             Node collection
   *
   */
  function getNgvCollection(iElement, selector) {
    var selectors,
        patt = /(.*?).ngv\((\w+)\)/,
        m;
    if (selector) {
      selectors = selector.trim().split(/\s*,\s*/);
      for (var i = 0; i < selectors.length; i++) {
        m = patt.exec(selectors[i]);
        if (m) {
          selectors[i] = m[1] + '.' + getNgvClass(m[2]);
        }
      }
      selector = selectors.join(',');
      return a.element(iElement[0].querySelectorAll(selector));
    } else {
      return iElement.children();
    }
  }

  /**
   * Base directive link function
   *
   * @param   object  scope     Scope object used for evaluation
   * @param   object  iElement  Element to perform animation on
   * @param   object  iAttrs    reference to directive attributes
   * @param   string  collectionAttr Attribute name of element child selector
   *
   */
  function ngvLink(scope, iElement, iAttrs, collectionAttr, noToggle) {
    // Unwatchers
    var unwatch = null;
    // Flags
    var repeat = a.isDefined(iAttrs.ngvOnce) ? iAttrs.ngvOnce === 'false' : true,
        eager = a.isDefined(iAttrs.ngvEager) ? iAttrs.ngvEager !== 'false' : false,
        animated = false,
        collection = false;
    // Props
    var effect = 'fade';
    /* Attempt to evaluate ngvEffect if specified */
    if (iAttrs.ngvEffect) {
      effect = scope.$eval(iAttrs.ngvEffect);
      if (!effect) {
        effect = iAttrs.ngvEffect;
      }
    }
    /* Check if collectionAttr is valid */
    if (a.isDefined(collectionAttr) && !a.isDefined(iAttrs[collectionAttr])) {
      collectionAttr = 'ngvSelector';
    }
    collection = a.isDefined(iAttrs[collectionAttr]);

    if (!noToggle && iAttrs.ngvToggle && effect) {
      scope.$watch(iAttrs.ngvToggle, function(n, o) {
        var element = collection ? getNgvCollection(iElement, iAttrs[collectionAttr]) : iElement;
        var options = getNgvOptions(scope, iAttrs, n);
        if (!eager && n === o) {
          if (n) {
            element.css({display: options.display || 'inherited', opacity: 1});
          } else {
            element.css({display: options.display || 'none', opacity: 0});
          }
        } else {
          v(element, 'stop');
          if (a.isString(effect)) {
            v(element, effect+(n ? 'In' : 'Out'), options);
          } else {
            if (animated) {
              v(element, 'reverse', options);
            } else {
              v(element, effect, options);
              animated = true;
            }
          }
        }
      });
    } else if (iAttrs.ngvAnimate && effect) {
      unwatch = scope.$watch(iAttrs.ngvAnimate, function (n) {
        if (n) {
          var element = collection ? getNgvCollection(iElement, iAttrs[collectionAttr]) : iElement;
          var options = getNgvOptions(scope, iAttrs);
          v(element, effect, options);
          if (!repeat) {
            unwatch();
          }
        }
      });
    } else if (iAttrs.ngvTrigger && effect) {
      unwatch = scope.$on(iAttrs.ngvTrigger, function () {
        var element = collection ? getNgvCollection(iElement, iAttrs[collectionAttr]) : iElement;
        var options = getNgvOptions(scope, iAttrs);
        v(element, effect, options);
        if (!repeat) {
          unwatch();
        }
      });
    }
  }

  /**
   * Angulocity Module
   *
   * @ngmodule Angulocity
   */
  a.module('Angulocity', [])
  .provider('$ngvAnimator', function ngvAnimatorProvider() {
    var sequences = [];
    var defaults = {

    };

    this.defaults = defaults;
    this.RegisterEffect = a.isDefined(v.RegisterEffect) ? v.RegisterEffect : a.noop;
    this.RegisterSequence = function(name, sequence) {
      if (name && a.isObject(sequence)) {
        sequence[getNgvClass] = sequence;
      }
    };
    this.$get = ['$document', function ($document) {
      return {
        Velocity: v,
        animate: function (klass, effect, options) {
          var collection = getNgvCollection($document, klass);
          if (collection) {
            return v(collection, effect, options);
          }
        },
        element: function (klass) {
          return getNgvCollection($document, klass);
        },
        animateSequence: function(arg) {
          var seq, iSeq;
          if (a.isString(arg)) {
            seq = sequences[getNgvClass(name)];
            iSeq = [];
            for (var i = 0 ; i < seq.length; i++) {
              // Generate correct sequence format
              iSeq.push({e: getNgvCollection($document, seq[i].class), p: seq[i].properties, o: seq[i].options});
            }
            return v.RunSequence(iSeq);
          } else if (a.isObject(arg)) {
            return v.RunSequence(arg);
          }
        }
      };
    }];
  })
  /**
   * @ngdoc directive
   * @name  ngvSlide
   *
   */
  .directive('ngvSlide', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = a.isDefined(iAttrs.ngvOnce) ? iAttrs.ngvOnce === 'false' : false;
        var options;

        // If animation switch provided
        if (iAttrs.ngvSlide) {
          scope.$watch(iAttrs.ngvSlide, function(n, o) {
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited'});
              } else {
                iElement.css({display: options.display || 'none'});
              }
            } else {
              if (n) {
                v(iElement, 'stop');
                v(iElement, 'slideDown', options);
              } else {
                v(iElement, 'stop');
                v(iElement, 'slideUp', options);
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, 'slideDown', options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, 'slideUp', options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvFade
   *
   */
  .directive('ngvFade', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = a.isDefined(iAttrs.ngvOnce) ? iAttrs.ngvOnce === 'false' : false;
        var options;

        // If animation switch provided
        if (iAttrs.ngvFade) {
          scope.$watch(iAttrs.ngvFade, function(n, o) {
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited', opacity: 1});
              } else {
                iElement.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              if (n) {
                v(iElement, 'stop');
                v(iElement, 'fadeIn', options);
              } else {
                v(iElement, 'stop');
                v(iElement, 'fadeOut', options);
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, 'fadeIn', options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, 'fadeOut', options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvClass
   *
   */
  .directive('ngvClass', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        iElement.addClass(getNgvClass(iAttrs.ngvClass));
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvAnimator
   *
   */
  .directive('ngvAnimator', ['$document', function ($document) {
    return {
      restrict      : 'EA',
      scope         :  true,
      link          : function (scope, iElement, iAttrs) {
        var element = a.isDefined(iAttrs.ngvGlobal) ? $document : iElement.parent();
        ngvLink(scope, element, iAttrs, 'ngvSelect', true);
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvCollection
   *
   */
  .directive('ngvCollection', [function () {
    return {
      restrict      : 'EA',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        ngvLink(scope, iElement, iAttrs, 'ngvCollection');
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvElement
   *
   */
  .directive('ngvElement', [function () {
    return {
      restrict      : 'EA',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        if (iAttrs.ngvElement) {
          iElement.addClass(getNgvClass(iAttrs.ngvElement));
        }
        ngvLink(scope, iElement, iAttrs);
      }
    };
  }])
  ;
})(window, void 0);
<commit_msg>with version<commit_after>/*! Angulocity 0.1.0. (C) 2015 John Cui. MIT @license: en.wikipedia.org/wiki/MIT_License */
/*jshint bitwise: false*/
;(function (window, undefined) {
  'use strict';

  /* Create some shortcut references to the libraries */
  var a = window.angular,
      v = window.Velocity || window.jQuery.Velocity,
      $ = $ ? $ : window.jQuery,
      VERSION = '0.1.0';

  /* Generate some error messages in the console if Velocity or Velocity UI is not found */
  if (!v) {
    console.error('Angulocity: Velocity animation engine is not found. Animations are disabled.');
    v = a.noop;
  } else if (!v.RegisterEffect) {
    console.error('Angulocity: Velocity UI Pack is not found. Additional animation effects will not be available.');
  }

  /* Detect IE and add appropriate angular ie shims */
  if (/MSIE/.test(window.navigator.userAgent)) {
    window.document.createElement('ngv-element');
    window.document.createElement('ngv-collection');
    window.document.createElement('ngv-animator');
  }

  /**
   * Setup Velocity options based on attributes passed to the
   * current element.
   *
   * @param   mixed   scope   Scope used to evaluate angular attribure expressions
   * @param   mixed   iAttrs  Attribute list
   * @param   bool    bIn     True if animating "In", false if animating "Out"
   *
   * @return  mixed           Velocity options object
   *
   */
  function getNgvOptions (scope, iAttrs, bIn) {
    var options = {
      /* Velocity's default options */
      duration: iAttrs.ngvDuration ? iAttrs.ngvDuration : 400,
      delay: iAttrs.ngvDelay ? scope.$eval(iAttrs.ngvDelay) : false,
      easing: iAttrs.ngvEasing ? iAttrs.ngvEasing : 'easeInOutQuart',
      queue: iAttrs.ngvQueue ? iAttrs.ngvQueue : undefined,
      begin: iAttrs.ngvBegin ? scope.$eval(iAttrs.ngvBegin) : undefined,
      progress: iAttrs.ngvProgress ? scope.$eval(iAttrs.ngvProgress) : undefined,
      complete: iAttrs.ngvComplete ? scope.$eval(iAttrs.ngvComplete) : undefined,
      display: iAttrs.ngvDisplay ? iAttrs.ngvDisplay : undefined,
      visibility: iAttrs.ngvVisibility ? iAttrs.ngvVisibility : undefined,
      loop: iAttrs.ngvLoop ? scope.$eval(iAttrs.ngvLoop) : undefined,
      stagger: iAttrs.ngvStagger ? scope.$eval(iAttrs.ngvStagger) : undefined,
      drag: iAttrs.ngvDrag ? scope.$eval(iAttrs.ngvDrag) : undefined,
      mobileHA: iAttrs.ngvMobileHa ? scope.$eval(iAttrs.ngvMobileHa) :  true
    };

    /* Alter some options based on whether animating "In" or "Out" */
    if (bIn === true) {
      options.display = iAttrs.ngvInDisplay ? iAttrs.ngvInDisplay : options.display;
      options.visibility = iAttrs.ngvInVisibility ? iAttrs.ngvInVisibility : options.visibility;
      options.duration = iAttrs.ngvInDuration ? iAttrs.ngvInDuration : options.duration;
      options.delay = iAttrs.ngvInDelay ? iAttrs.ngvInDelay : options.delay;
    } else if (bIn === false) {
      options.display = iAttrs.ngvOutDisplay ? iAttrs.ngvOutDisplay : options.display;
      options.visibility = iAttrs.ngvOutVisibility ? iAttrs.ngvOutVisibility : options.visibility;
      options.duration = iAttrs.ngvOutDuration ? iAttrs.ngvOutDuration : options.duration;
      options.delay = iAttrs.ngvOutDelay ? iAttrs.ngvOutDelay : options.delay;
    }

    return options;
  }

  /**
   * Utility function to generate hash
   *
   * @param   string  s       String to perform has on
   *
   * @return  string          Hashed value of string
   */
  function getNgvHash(s) {
    var i = 0, x = 0, c;
    for (; i < s.length; i++) {
      c = s.charCodeAt(i);
      x = ((x<<5)-x)+c;
    }
    x |= 0;
    return x.toString(16);
  }
  /**
   * Utility function to generate hash for class names.
   *
   * @param   string  klass   Whitespace separated class values
   *
   * @return  string          Hashed class values
   *
   */
  function getNgvClass(klass) {
    var i;
    klass = klass.trim().split(/\s/);
    for (i = 0; i < klass.length; i++) {
      klass[i] = 'ngv' + getNgvHash('ngv' + klass[i].toLowerCase());
    }
    return klass.join(' ');
  }

  /**
   * Get collection of nodes within an element via selector
   *
   * @param   element iElement  Root element node containing the collection
   * @param   string  selector  Query selector for finding child elements
   *
   * @return  array             Node collection
   *
   */
  function getNgvCollection(iElement, selector) {
    var selectors,
        patt = /(.*?).ngv\((\w+)\)/,
        m;
    if (selector) {
      selectors = selector.trim().split(/\s*,\s*/);
      for (var i = 0; i < selectors.length; i++) {
        m = patt.exec(selectors[i]);
        if (m) {
          selectors[i] = m[1] + '.' + getNgvClass(m[2]);
        }
      }
      selector = selectors.join(',');
      return a.element(iElement[0].querySelectorAll(selector));
    } else {
      return iElement.children();
    }
  }

  /**
   * Base directive link function
   *
   * @param   object  scope     Scope object used for evaluation
   * @param   object  iElement  Element to perform animation on
   * @param   object  iAttrs    reference to directive attributes
   * @param   string  collectionAttr Attribute name of element child selector
   *
   */
  function ngvLink(scope, iElement, iAttrs, collectionAttr, noToggle) {
    // Unwatchers
    var unwatch = null;
    // Flags
    var repeat = a.isDefined(iAttrs.ngvOnce) ? iAttrs.ngvOnce === 'false' : true,
        eager = a.isDefined(iAttrs.ngvEager) ? iAttrs.ngvEager !== 'false' : false,
        animated = false,
        collection = false;
    // Props
    var effect = 'fade';
    /* Attempt to evaluate ngvEffect if specified */
    if (iAttrs.ngvEffect) {
      effect = scope.$eval(iAttrs.ngvEffect);
      if (!effect) {
        effect = iAttrs.ngvEffect;
      }
    }
    /* Check if collectionAttr is valid */
    if (a.isDefined(collectionAttr) && !a.isDefined(iAttrs[collectionAttr])) {
      collectionAttr = 'ngvSelector';
    }
    collection = a.isDefined(iAttrs[collectionAttr]);

    if (!noToggle && iAttrs.ngvToggle && effect) {
      scope.$watch(iAttrs.ngvToggle, function(n, o) {
        var element = collection ? getNgvCollection(iElement, iAttrs[collectionAttr]) : iElement;
        var options = getNgvOptions(scope, iAttrs, n);
        if (!eager && n === o) {
          if (n) {
            element.css({display: options.display || 'inherited', opacity: 1});
          } else {
            element.css({display: options.display || 'none', opacity: 0});
          }
        } else {
          v(element, 'stop');
          if (a.isString(effect)) {
            v(element, effect+(n ? 'In' : 'Out'), options);
          } else {
            if (animated) {
              v(element, 'reverse', options);
            } else {
              v(element, effect, options);
              animated = true;
            }
          }
        }
      });
    } else if (iAttrs.ngvAnimate && effect) {
      unwatch = scope.$watch(iAttrs.ngvAnimate, function (n) {
        if (n) {
          var element = collection ? getNgvCollection(iElement, iAttrs[collectionAttr]) : iElement;
          var options = getNgvOptions(scope, iAttrs);
          v(element, effect, options);
          if (!repeat) {
            unwatch();
          }
        }
      });
    } else if (iAttrs.ngvTrigger && effect) {
      unwatch = scope.$on(iAttrs.ngvTrigger, function () {
        var element = collection ? getNgvCollection(iElement, iAttrs[collectionAttr]) : iElement;
        var options = getNgvOptions(scope, iAttrs);
        v(element, effect, options);
        if (!repeat) {
          unwatch();
        }
      });
    }
  }

  /**
   * Angulocity Module
   *
   * @ngmodule Angulocity
   */
  a.module('Angulocity', [])
  .provider('$ngvAnimator', function ngvAnimatorProvider() {
    var sequences = [];
    var defaults = {

    };

    this.defaults = defaults;
    this.RegisterEffect = a.isDefined(v.RegisterEffect) ? v.RegisterEffect : a.noop;
    this.RegisterSequence = function(name, sequence) {
      if (name && a.isObject(sequence)) {
        sequence[getNgvClass] = sequence;
      }
    };
    this.$get = ['$document', function ($document) {
      return {
        Velocity: v,
        animate: function (klass, effect, options) {
          var collection = getNgvCollection($document, klass);
          if (collection) {
            return v(collection, effect, options);
          }
        },
        element: function (klass) {
          return getNgvCollection($document, klass);
        },
        animateSequence: function(arg) {
          var seq, iSeq;
          if (a.isString(arg)) {
            seq = sequences[getNgvClass(name)];
            iSeq = [];
            for (var i = 0 ; i < seq.length; i++) {
              // Generate correct sequence format
              iSeq.push({e: getNgvCollection($document, seq[i].class), p: seq[i].properties, o: seq[i].options});
            }
            return v.RunSequence(iSeq);
          } else if (a.isObject(arg)) {
            return v.RunSequence(arg);
          }
        }
      };
    }];
  })
  /**
   * @ngdoc directive
   * @name  ngvSlide
   *
   */
  .directive('ngvSlide', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = a.isDefined(iAttrs.ngvOnce) ? iAttrs.ngvOnce === 'false' : false;
        var options;

        // If animation switch provided
        if (iAttrs.ngvSlide) {
          scope.$watch(iAttrs.ngvSlide, function(n, o) {
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited'});
              } else {
                iElement.css({display: options.display || 'none'});
              }
            } else {
              if (n) {
                v(iElement, 'stop');
                v(iElement, 'slideDown', options);
              } else {
                v(iElement, 'stop');
                v(iElement, 'slideUp', options);
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, 'slideDown', options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, 'slideUp', options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvFade
   *
   */
  .directive('ngvFade', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = a.isDefined(iAttrs.ngvOnce) ? iAttrs.ngvOnce === 'false' : false;
        var options;

        // If animation switch provided
        if (iAttrs.ngvFade) {
          scope.$watch(iAttrs.ngvFade, function(n, o) {
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited', opacity: 1});
              } else {
                iElement.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              if (n) {
                v(iElement, 'stop');
                v(iElement, 'fadeIn', options);
              } else {
                v(iElement, 'stop');
                v(iElement, 'fadeOut', options);
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, 'fadeIn', options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, 'fadeOut', options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvClass
   *
   */
  .directive('ngvClass', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        iElement.addClass(getNgvClass(iAttrs.ngvClass));
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvAnimator
   *
   */
  .directive('ngvAnimator', ['$document', function ($document) {
    return {
      restrict      : 'EA',
      scope         :  true,
      link          : function (scope, iElement, iAttrs) {
        var element = a.isDefined(iAttrs.ngvGlobal) ? $document : iElement.parent();
        ngvLink(scope, element, iAttrs, 'ngvSelect', true);
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvCollection
   *
   */
  .directive('ngvCollection', [function () {
    return {
      restrict      : 'EA',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        ngvLink(scope, iElement, iAttrs, 'ngvCollection');
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvElement
   *
   */
  .directive('ngvElement', [function () {
    return {
      restrict      : 'EA',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        if (iAttrs.ngvElement) {
          iElement.addClass(getNgvClass(iAttrs.ngvElement));
        }
        ngvLink(scope, iElement, iAttrs);
      }
    };
  }])
  ;
})(window, void 0);
<|endoftext|>


<commit_before>/*! Angulocity 0.1.0. (C) 2015 John Cui. MIT @license: en.wikipedia.org/wiki/MIT_License */
/*jshint bitwise: false*/
;(function (window, undefined) {
  'use strict';

  /* Create some shortcut references to the libraries */
  var a = window.angular,
      v = window.Velocity || window.jQuery.Velocity,
      $ = $ ? $ : window.jQuery;

  /* Generate some error messages in the console if Velocity or Velocity UI is not found */
  if (!v) {
    console.error('Angulocity: Velocity animation engine is not found. Animations are disabled.');
    v = a.noop;
  } else if (!v.RegisterEffect) {
    console.error('Angulocity: Velocity UI Pack is not found. Additional animation effects will not be available.');
  }

  /**
   * Setup Velocity options based on attributes passed to the
   * current element.
   *
   * @param   mixed   scope   Scope used to evaluate angular attribure expressions
   * @param   mixed   iAttrs  Attribute list
   * @param   bool    bIn     True if animating "In", false if animating "Out"
   *
   * @return  mixed           Velocity options object
   *
   */
  function getNgvOptions (scope, iAttrs, bIn) {
    var options = {
      /* Velocity's default options */
      duration: iAttrs.ngvDuration ? iAttrs.ngvDuration : 400,
      delay: iAttrs.ngvDelay ? scope.$eval(iAttrs.ngvDelay) : false,
      easing: iAttrs.ngvEasing ? iAttrs.ngvEasing : 'easeInOutQuart',
      queue: iAttrs.ngvQueue ? iAttrs.ngvQueue : undefined,
      begin: iAttrs.ngvBegin ? scope.$eval(iAttrs.ngvBegin) : undefined,
      progress: iAttrs.ngvProgress ? scope.$eval(iAttrs.ngvProgress) : undefined,
      complete: iAttrs.ngvComplete ? scope.$eval(iAttrs.ngvComplete) : undefined,
      display: iAttrs.ngvDisplay ? iAttrs.ngvDisplay : undefined,
      visibility: iAttrs.ngvVisibility ? iAttrs.ngvVisibility : undefined,
      loop: iAttrs.ngvLoop ? scope.$eval(iAttrs.ngvLoop) : undefined,
      stagger: iAttrs.ngvStagger ? scope.$eval(iAttrs.ngvStagger) : undefined,
      drag: iAttrs.ngvDrag ? scope.$eval(iAttrs.ngvDrag) : undefined,
      mobileHA: iAttrs.ngvMobileHa ? scope.$eval(iAttrs.ngvMobileHa) :  true
    };

    /* Alter some options based on whether animating "In" or "Out" */
    if (bIn === true) {
      options.display = iAttrs.ngvInDisplay ? iAttrs.ngvInDisplay : options.display;
      options.visibility = iAttrs.ngvInVisibility ? iAttrs.ngvInVisibility : options.visibility;
      options.duration = iAttrs.ngvInDuration ? iAttrs.ngvInDuration : options.duration;
      options.delay = iAttrs.ngvInDelay ? iAttrs.ngvInDelay : options.delay;
    } else if (bIn === false) {
      options.display = iAttrs.ngvOutDisplay ? iAttrs.ngvOutDisplay : options.display;
      options.visibility = iAttrs.ngvOutVisibility ? iAttrs.ngvOutVisibility : options.visibility;
      options.duration = iAttrs.ngvOutDuration ? iAttrs.ngvOutDuration : options.duration;
      options.delay = iAttrs.ngvOutDelay ? iAttrs.ngvOutDelay : options.delay;
    }

    return options;
  }

  /**
   * Utility function to generate hash for class names.
   *
   * @param   string  klass   Whitespace separated class values
   *
   * @return  string          Hashed class values
   *
   */
  function getNgvClass(klass) {
    var i, j, x, c, s;
    klass = klass.split(/\s/);
    for (i = 0; i < klass.length; i++) {
      x = 0;
      s = 'ngv' + klass[i].toLowerCase();
      for (j = 0; j < s.length; j++) {
          c = s.charCodeAt(i);
          x = ((x<<5)-x)+c;
      }
      klass[i] = 'ngv' + Math.abs(x).toString(16);
    }
    return klass.join(' ');
  }

  /**
   * Get collection of nodes within an element via selector
   *
   * @param   element iElement  Root element node containing the collection
   * @param   string  selector  Query selector for finding child elements
   *
   * @return  array             Node collection
   *
   */
  function getNgvCollection(iElement, selector) {
    var m = /^ngv:(\w+?)$/.exec(selector);
    if (m) {
      return a.element(iElement[0].querySelectorAll('.' + getNgvClass(m[1])));
    } else {
      return a.element(iElement[0].querySelectorAll(selector));
    }
  }

  /**
   * Angulocity Module
   *
   * @ngmodule Angulocity
   */
  a.module('Angulocity', [])
  .provider('$ngvAnimator', function ngvAnimatorProvider() {
    var defaults = {

    };

    this.defaults = defaults;
    this.RegisterEffect = a.isDefined(v.RegisterEffect) ? v.RegisterEffect : a.noop;
    this.$get = [function () {
      return null;
    }];
  })
  /**
   * @ngdoc directive
   * @name  ngvSlide
   *
   */
  .directive('ngvSlide', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = iAttrs.ngvRepeat !== 'false';
        var options;

        // If animation switch provided
        if (iAttrs.ngvSlide) {
          scope.$watch(iAttrs.ngvSlide, function(n, o) {
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited', opacity: 1});
              } else {
                iElement.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              if (n) {
                v(iElement, 'stop');
                v(iElement, 'slideDown', options);
              } else {
                v(iElement, 'stop');
                v(iElement, 'slideUp', options);
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, 'slideDown', options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, 'slideUp', options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvFade
   *
   */
  .directive('ngvFade', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = iAttrs.ngvRepeat !== 'false';
        var options;

        // If animation switch provided
        if (iAttrs.ngvFade) {
          scope.$watch(iAttrs.ngvFade, function(n, o) {
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited', opacity: 1});
              } else {
                iElement.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              options = getNgvOptions(scope, iAttrs, n);
              if (n) {
                v(iElement, 'stop');
                v(iElement, 'fadeIn', options);
              } else {
                v(iElement, 'stop');
                v(iElement, 'fadeOut', options);
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, 'fadeIn', options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, 'fadeOut', options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvCollection
   *
   */
  .directive('ngvCollection', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        var collection;
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = iAttrs.ngvRepeat !== 'false',
            effect,
            animated = false;
        var options;
        /* Attempt to evaluate effect */
        if (iAttrs.ngvEffect) {
          effect = scope.$eval(iAttrs.ngvEffect);
          if (!effect) {
            effect = iAttrs.ngvEffect;
          }
        } else {
          effect = 'fade';
        }

        if (iAttrs.ngvToggle && effect) {
          scope.$watch(iAttrs.ngvToggle, function(n, o) {
            collection = iAttrs.ngvCollection ? getNgvCollection(iElement, iAttrs.ngvCollection) : iElement.children();
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                collection.css({display: options.display || 'inherited', opacity: 1});
              } else {
                collection.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              options = getNgvOptions(scope, iAttrs, n);
              v(collection, 'stop');
              if (a.isString(effect)) {
                v(collection, effect+(n ? 'In' : 'Out'), options);
              } else {
                if (animated) {
                  v(collection, 'reverse', options);
                } else {
                  v(collection, effect, options);
                  animated = true;
                }
              }
            }
          });
        } else if (iAttrs.ngvAnimate && effect) {
          options = getNgvOptions(scope, iAttrs);
          ngvIn = scope.$watch(iAttrs.ngvAnimate, function (n) {
            if (n) {
              v(collection, effect, options);
              if (!repeat) {
                ngvIn();
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(collection, effect, options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(collection, effect, options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvClass
   *
   */
  .directive('ngvClass', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        iElement.addClass(getNgvClass(iAttrs.ngvClass));
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvElement
   *
   */
  .directive('ngvElement', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = iAttrs.ngvRepeat !== 'false',
            effect,
            animated = false;
        var options;
        /* Attempt to evaluate effect */
        if (iAttrs.ngvEffect) {
          effect = scope.$eval(iAttrs.ngvEffect);
          if (!effect) {
            effect = iAttrs.ngvEffect;
          }
        } else {
          effect = 'fade';
        }

        if (iAttrs.ngvElement) {
          iElement.addClass(getNgvClass(iAttrs.ngvElement));
        }

        /* Set up watchers depending on element attributes passed */
        if (iAttrs.ngvToggle && effect) {
          scope.$watch(iAttrs.ngvToggle, function(n, o) {
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited', opacity: 1});
              } else {
                iElement.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              options = getNgvOptions(scope, iAttrs, n);
              v(iElement, 'stop');
              if (a.isString(effect)) {
                v(iElement, effect+(n ? 'In' : 'Out'), options);
              } else {
                if (animated) {
                  v(iElement, 'reverse', options);
                } else {
                  v(iElement, effect, options);
                  animated = true;
                }
              }
            }
          });
        } else if (iAttrs.ngvAnimate && effect) {
          options = getNgvOptions(scope, iAttrs);
          ngvIn = scope.$watch(iAttrs.ngvAnimate, function (n) {
            if (n) {
              v(iElement, effect, options);
              if (!repeat) {
                ngvIn();
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, effect, options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, effect, options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  ;
})(window, void 0);
<commit_msg>mixup<commit_after>/*! Angulocity 0.1.0. (C) 2015 John Cui. MIT @license: en.wikipedia.org/wiki/MIT_License */
/*jshint bitwise: false*/
;(function (window, undefined) {
  'use strict';

  /* Create some shortcut references to the libraries */
  var a = window.angular,
      v = window.Velocity || window.jQuery.Velocity,
      $ = $ ? $ : window.jQuery;

  /* Generate some error messages in the console if Velocity or Velocity UI is not found */
  if (!v) {
    console.error('Angulocity: Velocity animation engine is not found. Animations are disabled.');
    v = a.noop;
  } else if (!v.RegisterEffect) {
    console.error('Angulocity: Velocity UI Pack is not found. Additional animation effects will not be available.');
  }

  /**
   * Setup Velocity options based on attributes passed to the
   * current element.
   *
   * @param   mixed   scope   Scope used to evaluate angular attribure expressions
   * @param   mixed   iAttrs  Attribute list
   * @param   bool    bIn     True if animating "In", false if animating "Out"
   *
   * @return  mixed           Velocity options object
   *
   */
  function getNgvOptions (scope, iAttrs, bIn) {
    var options = {
      /* Velocity's default options */
      duration: iAttrs.ngvDuration ? iAttrs.ngvDuration : 400,
      delay: iAttrs.ngvDelay ? scope.$eval(iAttrs.ngvDelay) : false,
      easing: iAttrs.ngvEasing ? iAttrs.ngvEasing : 'easeInOutQuart',
      queue: iAttrs.ngvQueue ? iAttrs.ngvQueue : undefined,
      begin: iAttrs.ngvBegin ? scope.$eval(iAttrs.ngvBegin) : undefined,
      progress: iAttrs.ngvProgress ? scope.$eval(iAttrs.ngvProgress) : undefined,
      complete: iAttrs.ngvComplete ? scope.$eval(iAttrs.ngvComplete) : undefined,
      display: iAttrs.ngvDisplay ? iAttrs.ngvDisplay : undefined,
      visibility: iAttrs.ngvVisibility ? iAttrs.ngvVisibility : undefined,
      loop: iAttrs.ngvLoop ? scope.$eval(iAttrs.ngvLoop) : undefined,
      stagger: iAttrs.ngvStagger ? scope.$eval(iAttrs.ngvStagger) : undefined,
      drag: iAttrs.ngvDrag ? scope.$eval(iAttrs.ngvDrag) : undefined,
      mobileHA: iAttrs.ngvMobileHa ? scope.$eval(iAttrs.ngvMobileHa) :  true
    };

    /* Alter some options based on whether animating "In" or "Out" */
    if (bIn === true) {
      options.display = iAttrs.ngvInDisplay ? iAttrs.ngvInDisplay : options.display;
      options.visibility = iAttrs.ngvInVisibility ? iAttrs.ngvInVisibility : options.visibility;
      options.duration = iAttrs.ngvInDuration ? iAttrs.ngvInDuration : options.duration;
      options.delay = iAttrs.ngvInDelay ? iAttrs.ngvInDelay : options.delay;
    } else if (bIn === false) {
      options.display = iAttrs.ngvOutDisplay ? iAttrs.ngvOutDisplay : options.display;
      options.visibility = iAttrs.ngvOutVisibility ? iAttrs.ngvOutVisibility : options.visibility;
      options.duration = iAttrs.ngvOutDuration ? iAttrs.ngvOutDuration : options.duration;
      options.delay = iAttrs.ngvOutDelay ? iAttrs.ngvOutDelay : options.delay;
    }

    return options;
  }

  /**
   * Utility function to generate hash for class names.
   *
   * @param   string  klass   Whitespace separated class values
   *
   * @return  string          Hashed class values
   *
   */
  function getNgvClass(klass) {
    var i, j, x, c, s;
    klass = klass.split(/\s/);
    for (i = 0; i < klass.length; i++) {
      x = 0;
      s = 'ngv' + klass[i].toLowerCase();
      for (j = 0; j < s.length; j++) {
          c = s.charCodeAt(i);
          x = ((x<<5)-x)+c;
      }
      klass[i] = 'ngv' + Math.abs(x).toString(16);
    }
    return klass.join(' ');
  }

  /**
   * Get collection of nodes within an element via selector
   *
   * @param   element iElement  Root element node containing the collection
   * @param   string  selector  Query selector for finding child elements
   *
   * @return  array             Node collection
   *
   */
  function getNgvCollection(iElement, selector) {
    var m = /^ngv:(\w+?)$/.exec(selector);
    if (m) {
      return a.element(iElement[0].querySelectorAll('.' + getNgvClass(m[1])));
    } else {
      return a.element(iElement[0].querySelectorAll(selector));
    }
  }

  /**
   * Angulocity Module
   *
   * @ngmodule Angulocity
   *
   */
  a.module('Angulocity', [])
  .provider('$ngvAnimator', function ngvAnimatorProvider() {
    var defaults = {

    };

    this.defaults = defaults;
    this.RegisterEffect = a.isDefined(v.RegisterEffect) ? v.RegisterEffect : a.noop;
    this.$get = [function () {
      return null;
    }];
  })
  /**
   * @ngdoc directive
   * @name  ngvSlide
   *
   */
  .directive('ngvSlide', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = iAttrs.ngvRepeat !== 'false';
        var options;

        // If animation switch provided
        if (iAttrs.ngvSlide) {
          scope.$watch(iAttrs.ngvSlide, function(n, o) {
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited', opacity: 1});
              } else {
                iElement.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              if (n) {
                v(iElement, 'stop');
                v(iElement, 'slideDown', options);
              } else {
                v(iElement, 'stop');
                v(iElement, 'slideUp', options);
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, 'slideDown', options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, 'slideUp', options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvFade
   *
   */
  .directive('ngvFade', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = iAttrs.ngvRepeat !== 'false';
        var options;

        // If animation switch provided
        if (iAttrs.ngvFade) {
          scope.$watch(iAttrs.ngvFade, function(n, o) {
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited', opacity: 1});
              } else {
                iElement.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              if (n) {
                v(iElement, 'stop');
                v(iElement, 'fadeIn', options);
              } else {
                v(iElement, 'stop');
                v(iElement, 'fadeOut', options);
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, 'fadeIn', options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, 'fadeOut', options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvClass
   *
   */
  .directive('ngvClass', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        iElement.addClass(getNgvClass(iAttrs.ngvClass));
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvCollection
   *
   */
  .directive('ngvCollection', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        var collection;
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = iAttrs.ngvRepeat !== 'false',
            effect,
            animated = false;
        var options;
        /* Attempt to evaluate effect */
        if (iAttrs.ngvEffect) {
          effect = scope.$eval(iAttrs.ngvEffect);
          if (!effect) {
            effect = iAttrs.ngvEffect;
          }
        } else {
          effect = 'fade';
        }

        if (iAttrs.ngvToggle && effect) {
          scope.$watch(iAttrs.ngvToggle, function(n, o) {
            collection = iAttrs.ngvCollection ? getNgvCollection(iElement, iAttrs.ngvCollection) : iElement.children();
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                collection.css({display: options.display || 'inherited', opacity: 1});
              } else {
                collection.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              v(collection, 'stop');
              if (a.isString(effect)) {
                v(collection, effect+(n ? 'In' : 'Out'), options);
              } else {
                if (animated) {
                  v(collection, 'reverse', options);
                } else {
                  v(collection, effect, options);
                  animated = true;
                }
              }
            }
          });
        } else if (iAttrs.ngvAnimate && effect) {
          options = getNgvOptions(scope, iAttrs);
          ngvIn = scope.$watch(iAttrs.ngvAnimate, function (n) {
            if (n) {
              v(collection, effect, options);
              if (!repeat) {
                ngvIn();
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(collection, effect, options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(collection, effect, options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  /**
   * @ngdoc directive
   * @name  ngvElement
   *
   */
  .directive('ngvElement', [function () {
    return {
      restrict      : 'A',
      scope         :  false,
      link          : function (scope, iElement, iAttrs) {
        // Unwatchers
        var ngvIn = null,
            ngvOut = null;
        var repeat = iAttrs.ngvRepeat !== 'false',
            effect,
            animated = false;
        var options;
        /* Attempt to evaluate effect */
        if (iAttrs.ngvEffect) {
          effect = scope.$eval(iAttrs.ngvEffect);
          if (!effect) {
            effect = iAttrs.ngvEffect;
          }
        } else {
          effect = 'fade';
        }

        if (iAttrs.ngvElement) {
          iElement.addClass(getNgvClass(iAttrs.ngvElement));
        }

        /* Set up watchers depending on element attributes passed */
        if (iAttrs.ngvToggle && effect) {
          scope.$watch(iAttrs.ngvToggle, function(n, o) {
            options = getNgvOptions(scope, iAttrs, n);
            if (n === o) {
              if (n) {
                iElement.css({display: options.display || 'inherited', opacity: 1});
              } else {
                iElement.css({display: options.display || 'none', opacity: 0});
              }
            } else {
              v(iElement, 'stop');
              if (a.isString(effect)) {
                v(iElement, effect+(n ? 'In' : 'Out'), options);
              } else {
                if (animated) {
                  v(iElement, 'reverse', options);
                } else {
                  v(iElement, effect, options);
                  animated = true;
                }
              }
            }
          });
        } else if (iAttrs.ngvAnimate && effect) {
          options = getNgvOptions(scope, iAttrs);
          ngvIn = scope.$watch(iAttrs.ngvAnimate, function (n) {
            if (n) {
              v(iElement, effect, options);
              if (!repeat) {
                ngvIn();
              }
            }
          });
        } else {
          options = getNgvOptions(scope, iAttrs, true);
          if (iAttrs.ngvIn) {
            ngvIn = scope.$watch(iAttrs.ngvIn, function (n) {
              if (n) {
                v(iElement, effect, options);
                if (!repeat) {
                  ngvIn();
                }
              }
            });
          }
          options = getNgvOptions(scope, iAttrs, false);
          if (iAttrs.ngvOut) {
            ngvOut = scope.$watch(iAttrs.ngvOut, function(n) {
              if (n) {
                v(iElement, effect, options);
                if (!repeat) {
                  ngvOut();
                }
              }
            });
          }
        }
      }
    };
  }])
  ;
})(window, void 0);
<|endoftext|>


<commit_before><commit_msg>edge case fix<commit_after><|endoftext|>


<commit_before>/**
 * @file DropdownSelect component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Dropdown from '../Dropdown';
import TextField from '../TextField';
import List from '../List';
import DynamicRenderList from '../DynamicRenderList';
import Checkbox from '../Checkbox';
import Theme from '../Theme';

import SelectMode from '../_statics/SelectMode';
import Position from '../_statics/Position';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import ComponentUtil from '../_vendors/ComponentUtil';

class DropdownSelect extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;
    static Position = Position;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value,
            filter: '',
            popupVisible: false
        };

    }

    closePopup = () => {
        this.refs.dropdown && this.refs.dropdown.closePopup();
    };

    filterChangeHandler = filter => {
        this.setState({
            filter
        }, () => {
            const el = this.refs.dropdown;
            el && el.resetPopupPosition();
        });
    };

    filterData = (filter = this.state.filter, data = this.props.data) => {

        if (!filter) {
            return data;
        }

        const {displayField, isGrouped} = this.props,
            filterFunc = originData => originData.filter(item => typeof item === 'object' && !!item[displayField] ?
                item[displayField].toString().toUpperCase().includes(filter.toUpperCase())
                :
                item.toString().toUpperCase().includes(filter.toUpperCase()));

        if (isGrouped) {

            let result = Object.assign(data);

            for (let i = 0, len = result.length; i < len; i++) {
                let group = result[i];
                group.children = filterFunc(group.children);
                if (group.children.length < 1) {
                    result.splice(i, 1);
                    i--;
                }
            }

        }

        return filterFunc(data);

    };

    selectAllClickHandler = () => {

        const {data} = this.props,
            {value} = this.state;

        if (!data) {
            return;
        }

        const isSelectAll = !value || (value && value.length < data.length),
            newValue = isSelectAll ? data : [];

        this.setState({
            value: newValue
        }, () => {
            this.changeHandler(newValue);
        });

    };

    itemClickHandler = (...args) => {

        const {autoClose, onItemClick} = this.props;

        if (autoClose) {
            this.closePopup();
        }

        onItemClick && onItemClick(...args);

    };

    changeHandler = value => {

        const {autoClose} = this.props;
        if (autoClose) {
            this.closePopup();
        }

        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

        setTimeout(() => {
            this.refs.hiddenFilter && this.refs.hiddenFilter.focus();
        }, 1000);
    };

    popupOpenHandler = e => {

        const {isHiddenInputFilter, useFilter} = this.props;

        if (isHiddenInputFilter) {
            this.refs.hiddenFilter && this.refs.hiddenFilter.focus();
        } else if (useFilter) {
            this.refs.filter && this.refs.filter.focus();
        }

        this.setState({
            popupVisible: true
        }, () => {
            const {onOpenPopup} = this.props;
            onOpenPopup && onOpenPopup(e);
        });

    };

    popupCloseHandler = e => {

        this.refs.hiddenFilter && this.refs.hiddenFilter.blur();

        this.setState({
            popupVisible: false
        }, () => {
            const {onClosePopup} = this.props;
            onClosePopup && onClosePopup(e);
        });
    };

    hiddenFilterChangeHandle = e => {

        const {data, displayField, clearHiddenInputFilterInterval} = this.props;

        if (!data) {
            return;
        }

        let target = e.target,
            timer = setTimeout(() => {
                clearTimeout(timer);
                target.value = '';
            }, clearHiddenInputFilterInterval);

        let index = data.findIndex(item =>
            typeof item === 'object' ?
                item[displayField].toUpperCase().startsWith(target.value.toUpperCase())
                :
                item.toUpperCase().startsWith(target.value.toUpperCase())
        );

        this.scrollTo(this.refs.dropdownSelectListScroller, (index) * 40, 200);

    };

    scrollTo = (element, to, duration) => {

        if (!element) {
            return;
        }

        // jump to target if duration zero
        if (duration <= 0) {
            element.scrollTop = to;
            return;
        }

        const difference = to - element.scrollTop,
            perTick = difference / duration * 10;

        setTimeout(() => {
            element.scrollTop = element.scrollTop + perTick;
            if (element.scrollTop === to) return;
            this.scrollTo(element, to, duration - 10);
        }, 10);

    };

    getTriggerValue = () => {

        const {placeholder, triggerRenderer, renderer, valueField, displayField, selectMode} = this.props,
            {value} = this.state,
            isMultiSelect = selectMode === SelectMode.MULTI_SELECT;

        if (triggerRenderer) {
            return typeof triggerRenderer === 'function' ? triggerRenderer(value) : triggerRenderer;
        }

        if (!value) {
            return placeholder;
        }

        return isMultiSelect ?
            (
                value.length > 0 ?
                    value.length + ' selected'
                    :
                    placeholder
            )
            :
            (
                renderer ?
                    renderer(value)
                    :
                    Util.getTextByDisplayField(value, displayField, valueField)
            );

    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };
    }

    render() {

        const {

                className, triggerClassName, popupClassName, style, name, popupTheme, data, triggerRenderer,
                useDynamicRenderList, listHeight, itemHeight, scrollBuffer, renderer, selectMode,
                useFilter, filterIconCls, useSelectAll, selectAllText, valueField, displayField, descriptionField,
                popupChildren, isHiddenInputFilter, noMatchedMsg, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                ...restProps

            } = this.props,
            {value, filter, popupVisible} = this.state,

            isMultiSelect = selectMode === SelectMode.MULTI_SELECT,

            selectClassName = classNames('dropdown-select', {
                activated: popupVisible,
                [className]: className
            }),
            dropdownTriggerClassName = classNames({
                empty: !triggerRenderer && !value,
                [triggerClassName]: triggerClassName
            }),

            listData = this.filterData();

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={Util.getValueByValueField(value, valueField, displayField)}/>
                        :
                        null
                }

                {
                    isHiddenInputFilter ?
                        <input ref="hiddenFilter"
                               className="hiddenFilter"
                               type="text"
                               onChange={this.hiddenFilterChangeHandle}/>
                        :
                        null
                }

                <Dropdown {...restProps}
                          ref="dropdown"
                          triggerClassName={dropdownTriggerClassName}
                          popupClassName={'dropdown-select-popup' + (popupClassName ? ' ' + popupClassName : '')}
                          popupTheme={popupTheme}
                          triggerValue={this.getTriggerValue()}
                          onOpenPopup={this.popupOpenHandler}
                          onClosePopup={this.popupCloseHandler}>

                        {
                            useFilter ?
                                <TextField ref="filter"
                                           className="dropdown-select-filter"
                                           value={filter}
                                           rightIconCls={filterIconCls}
                                           onChange={this.filterChangeHandler}/>
                                :
                                null
                        }

                        {
                            isMultiSelect && useSelectAll ?

                                    <Checkbox className="list-item-select"
                                              checked={data && value && value.length === data.length}
                                              indeterminate={data && value && value.length > 0 && value.length < data.length}
                                              uncheckedIconCls={checkboxUncheckedIconCls}
                                              checkedIconCls={checkboxCheckedIconCls}
                                              indeterminateIconCls={checkboxIndeterminateIconCls}/>
                                    {selectAllText}

                                :
                                null
                        }

 Event.wheelHandler(e, this.props)}>

                        {
                            useFilter ?

                                :
                                null
                        }

                        {
                            isMultiSelect && useSelectAll ?

                                :
                                null
                        }

                        {
                            listData.length < 1 ?

                                    {
                                        noMatchedMsg ?
                                            noMatchedMsg
                                            :

                                                No matched value.

                                    }

                                :
                                useDynamicRenderList ?
                                    <DynamicRenderList className="dropdown-select-list"
                                                       theme={popupTheme}
                                                       selectMode={selectMode}
                                                       data={listData}
                                                       value={value}
                                                       valueField={valueField}
                                                       displayField={displayField}
                                                       descriptionField={descriptionField}
                                                       listHeight={listHeight}
                                                       itemHeight={itemHeight}
                                                       scrollBuffer={scrollBuffer}
                                                       renderer={renderer}
                                                       radioUncheckedIconCls={radioUncheckedIconCls}
                                                       radioCheckedIconCls={radioCheckedIconCls}
                                                       checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                                                       checkboxCheckedIconCls={checkboxCheckedIconCls}
                                                       checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                                                       onItemClick={this.itemClickHandler}
                                                       onChange={this.changeHandler}/>
                                    :
                                    <List className="dropdown-select-list"
                                          theme={popupTheme}
                                          selectMode={selectMode}
                                          data={listData}
                                          value={value}
                                          valueField={valueField}
                                          displayField={displayField}
                                          descriptionField={descriptionField}
                                          renderer={renderer}
                                          radioUncheckedIconCls={radioUncheckedIconCls}
                                          radioCheckedIconCls={radioCheckedIconCls}
                                          checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                                          checkboxCheckedIconCls={checkboxCheckedIconCls}
                                          checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                                          onItemClick={this.itemClickHandler}
                                          onChange={this.changeHandler}/>
                        }

                    {popupChildren}

                </Dropdown>

        );

    }
}

DropdownSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the trigger element.
     */
    triggerClassName: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme.
     */
    popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * The name of the dropDownSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the dropDownSelect.
     */
    value: PropTypes.any,

    /**
     * The placeholder of the dropDownSelect.
     */
    placeholder: PropTypes.string,

    title: PropTypes.string,

    triggerRenderer: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.func]),

    rightIconCls: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            title: PropTypes.string,

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            renderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onClick: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * The invalid message of dropDownSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the dropDownSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The select mode of listItem.Can be normal,checkbox.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the dropDownSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * If true,the drop-down box will have search input.
     */
    useFilter: PropTypes.bool,

    filterIconCls: PropTypes.string,
    useSelectAll: PropTypes.bool,
    selectAllText: PropTypes.string,

    /**
     * The message of no matching option.
     */
    noMatchedMsg: PropTypes.string,

    shouldPreventContainerScroll: PropTypes.bool,

    popupChildren: PropTypes.any,

    useDynamicRenderList: PropTypes.bool,
    listHeight: PropTypes.number,
    itemHeight: PropTypes.number,
    scrollBuffer: PropTypes.number,

    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    renderer: PropTypes.func,

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onItemClick: PropTypes.func,

    /**
     * Callback function fired when the popup is open.
     */
    onOpenPopup: PropTypes.func,

    /**
     * Callback function fired when the popup is close.
     */
    onClosePopup: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func,

    onWheel: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func,
    isHiddenInputFilter: PropTypes.bool,
    clearHiddenInputFilterInterval: PropTypes.number

};

DropdownSelect.defaultProps = {

    theme: Theme.DEFAULT,
    popupTheme: Theme.DEFAULT,

    placeholder: 'Please select ...',
    rightIconCls: 'fas fa-angle-down',
    data: [],
    disabled: false,
    selectMode: SelectMode.SINGLE_SELECT,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    autoClose: true,
    useFilter: false,
    filterIconCls: 'fas fa-search',
    useSelectAll: false,
    selectAllText: 'Select All',
    shouldPreventContainerScroll: true,
    useDynamicRenderList: false,
    isHiddenInputFilter: false,
    clearHiddenInputFilterInterval: 1000

};

export default DropdownSelect;<commit_msg>update DropdownSelect<commit_after>/**
 * @file DropdownSelect component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Dropdown from '../Dropdown';
import TextField from '../TextField';
import List from '../List';
import DynamicRenderList from '../DynamicRenderList';
import Checkbox from '../Checkbox';
import Theme from '../Theme';

import SelectMode from '../_statics/SelectMode';
import Position from '../_statics/Position';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import ComponentUtil from '../_vendors/ComponentUtil';

class DropdownSelect extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;
    static Position = Position;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value,
            filter: '',
            popupVisible: false
        };

    }

    /**
     * public
     */
    startRipple = e => {
        this.refs.dropdown && this.refs.dropdown.startRipple(e);
    };

    /**
     * public
     */
    endRipple = () => {
        this.refs.dropdown && this.refs.dropdown.endRipple();
    };

    /**
     * public
     */
    triggerRipple = e => {
        this.refs.dropdown && this.refs.dropdown.triggerRipple(e);
    };

    /**
     * public
     */
    resetPopupPosition = () => {
        this.refs.dropdown && this.refs.dropdown.resetPosition();
    };

    /**
     * public
     */
    closePopup = () => {
        this.refs.dropdown && this.refs.dropdown.closePopup();
    };

    filterChangeHandler = filter => {
        this.setState({
            filter
        }, () => {
            const el = this.refs.dropdown;
            el && el.resetPopupPosition();
        });
    };

    filterData = (filter = this.state.filter, data = this.props.data) => {

        if (!filter) {
            return data;
        }

        const {displayField, isGrouped} = this.props,
            filterFunc = originData => originData.filter(item => typeof item === 'object' && !!item[displayField] ?
                item[displayField].toString().toUpperCase().includes(filter.toUpperCase())
                :
                item.toString().toUpperCase().includes(filter.toUpperCase()));

        if (isGrouped) {

            let result = Object.assign(data);

            for (let i = 0, len = result.length; i < len; i++) {
                let group = result[i];
                group.children = filterFunc(group.children);
                if (group.children.length < 1) {
                    result.splice(i, 1);
                    i--;
                }
            }

        }

        return filterFunc(data);

    };

    selectAllClickHandler = () => {

        const {data} = this.props,
            {value} = this.state;

        if (!data) {
            return;
        }

        const isSelectAll = !value || (value && value.length < data.length),
            newValue = isSelectAll ? data : [];

        this.setState({
            value: newValue
        }, () => {
            this.changeHandler(newValue);
        });

    };

    itemClickHandler = (...args) => {

        const {autoClose, onItemClick} = this.props;

        if (autoClose) {
            this.closePopup();
        }

        onItemClick && onItemClick(...args);

    };

    changeHandler = value => {

        const {autoClose} = this.props;
        if (autoClose) {
            this.closePopup();
        }

        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

        setTimeout(() => {
            this.refs.hiddenFilter && this.refs.hiddenFilter.focus();
        }, 1000);
    };

    popupOpenHandler = e => {

        const {isHiddenInputFilter, useFilter} = this.props;

        if (isHiddenInputFilter) {
            this.refs.hiddenFilter && this.refs.hiddenFilter.focus();
        } else if (useFilter) {
            this.refs.filter && this.refs.filter.focus();
        }

        this.setState({
            popupVisible: true
        }, () => {
            const {onOpenPopup} = this.props;
            onOpenPopup && onOpenPopup(e);
        });

    };

    popupCloseHandler = e => {

        this.refs.hiddenFilter && this.refs.hiddenFilter.blur();

        this.setState({
            popupVisible: false
        }, () => {
            const {onClosePopup} = this.props;
            onClosePopup && onClosePopup(e);
        });
    };

    hiddenFilterChangeHandle = e => {

        const {data, displayField, clearHiddenInputFilterInterval} = this.props;

        if (!data) {
            return;
        }

        let target = e.target,
            timer = setTimeout(() => {
                clearTimeout(timer);
                target.value = '';
            }, clearHiddenInputFilterInterval);

        let index = data.findIndex(item =>
            typeof item === 'object' ?
                item[displayField].toUpperCase().startsWith(target.value.toUpperCase())
                :
                item.toUpperCase().startsWith(target.value.toUpperCase())
        );

        this.scrollTo(this.refs.dropdownSelectListScroller, (index) * 40, 200);

    };

    scrollTo = (element, to, duration) => {

        if (!element) {
            return;
        }

        // jump to target if duration zero
        if (duration <= 0) {
            element.scrollTop = to;
            return;
        }

        const difference = to - element.scrollTop,
            perTick = difference / duration * 10;

        setTimeout(() => {
            element.scrollTop = element.scrollTop + perTick;
            if (element.scrollTop === to) return;
            this.scrollTo(element, to, duration - 10);
        }, 10);

    };

    getTriggerValue = () => {

        const {placeholder, triggerRenderer, renderer, valueField, displayField, selectMode} = this.props,
            {value} = this.state,
            isMultiSelect = selectMode === SelectMode.MULTI_SELECT;

        if (triggerRenderer) {
            return typeof triggerRenderer === 'function' ? triggerRenderer(value) : triggerRenderer;
        }

        if (!value) {
            return placeholder;
        }

        return isMultiSelect ?
            (
                value.length > 0 ?
                    value.length + ' selected'
                    :
                    placeholder
            )
            :
            (
                renderer ?
                    renderer(value)
                    :
                    Util.getTextByDisplayField(value, displayField, valueField)
            );

    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };
    }

    render() {

        const {

                className, triggerClassName, popupClassName, style, name, popupTheme, data, triggerRenderer,
                useDynamicRenderList, listHeight, itemHeight, scrollBuffer, renderer, selectMode,
                useFilter, filterIconCls, useSelectAll, selectAllText, valueField, displayField, descriptionField,
                popupChildren, isHiddenInputFilter, noMatchedMsg, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                ...restProps

            } = this.props,
            {value, filter, popupVisible} = this.state,

            isMultiSelect = selectMode === SelectMode.MULTI_SELECT,

            selectClassName = classNames('dropdown-select', {
                activated: popupVisible,
                [className]: className
            }),
            dropdownTriggerClassName = classNames({
                empty: !triggerRenderer && !value,
                [triggerClassName]: triggerClassName
            }),

            listData = this.filterData();

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={Util.getValueByValueField(value, valueField, displayField)}/>
                        :
                        null
                }

                {
                    isHiddenInputFilter ?
                        <input ref="hiddenFilter"
                               className="hiddenFilter"
                               type="text"
                               onChange={this.hiddenFilterChangeHandle}/>
                        :
                        null
                }

                <Dropdown {...restProps}
                          ref="dropdown"
                          triggerClassName={dropdownTriggerClassName}
                          popupClassName={'dropdown-select-popup' + (popupClassName ? ' ' + popupClassName : '')}
                          popupTheme={popupTheme}
                          triggerValue={this.getTriggerValue()}
                          onOpenPopup={this.popupOpenHandler}
                          onClosePopup={this.popupCloseHandler}>

                        {
                            useFilter ?
                                <TextField ref="filter"
                                           className="dropdown-select-filter"
                                           value={filter}
                                           rightIconCls={filterIconCls}
                                           onChange={this.filterChangeHandler}/>
                                :
                                null
                        }

                        {
                            isMultiSelect && useSelectAll ?

                                    <Checkbox className="list-item-select"
                                              checked={data && value && value.length === data.length}
                                              indeterminate={data && value && value.length > 0 && value.length < data.length}
                                              uncheckedIconCls={checkboxUncheckedIconCls}
                                              checkedIconCls={checkboxCheckedIconCls}
                                              indeterminateIconCls={checkboxIndeterminateIconCls}/>
                                    {selectAllText}

                                :
                                null
                        }

 Event.wheelHandler(e, this.props)}>

                        {
                            useFilter ?

                                :
                                null
                        }

                        {
                            isMultiSelect && useSelectAll ?

                                :
                                null
                        }

                        {
                            listData.length < 1 ?

                                    {
                                        noMatchedMsg ?
                                            noMatchedMsg
                                            :

                                                No matched value.

                                    }

                                :
                                useDynamicRenderList ?
                                    <DynamicRenderList className="dropdown-select-list"
                                                       theme={popupTheme}
                                                       selectMode={selectMode}
                                                       data={listData}
                                                       value={value}
                                                       valueField={valueField}
                                                       displayField={displayField}
                                                       descriptionField={descriptionField}
                                                       listHeight={listHeight}
                                                       itemHeight={itemHeight}
                                                       scrollBuffer={scrollBuffer}
                                                       renderer={renderer}
                                                       radioUncheckedIconCls={radioUncheckedIconCls}
                                                       radioCheckedIconCls={radioCheckedIconCls}
                                                       checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                                                       checkboxCheckedIconCls={checkboxCheckedIconCls}
                                                       checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                                                       onItemClick={this.itemClickHandler}
                                                       onChange={this.changeHandler}/>
                                    :
                                    <List className="dropdown-select-list"
                                          theme={popupTheme}
                                          selectMode={selectMode}
                                          data={listData}
                                          value={value}
                                          valueField={valueField}
                                          displayField={displayField}
                                          descriptionField={descriptionField}
                                          renderer={renderer}
                                          radioUncheckedIconCls={radioUncheckedIconCls}
                                          radioCheckedIconCls={radioCheckedIconCls}
                                          checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                                          checkboxCheckedIconCls={checkboxCheckedIconCls}
                                          checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                                          onItemClick={this.itemClickHandler}
                                          onChange={this.changeHandler}/>
                        }

                    {popupChildren}

                </Dropdown>

        );

    }
}

DropdownSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the trigger element.
     */
    triggerClassName: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme.
     */
    popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    /**
     * The name of the dropDownSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the dropDownSelect.
     */
    value: PropTypes.any,

    /**
     * The placeholder of the dropDownSelect.
     */
    placeholder: PropTypes.string,

    title: PropTypes.string,

    triggerRenderer: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.func]),

    rightIconCls: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            title: PropTypes.string,

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            renderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onClick: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * The invalid message of dropDownSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the dropDownSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The select mode of listItem.Can be normal,checkbox.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the dropDownSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * If true,the drop-down box will have search input.
     */
    useFilter: PropTypes.bool,

    filterIconCls: PropTypes.string,
    useSelectAll: PropTypes.bool,
    selectAllText: PropTypes.string,

    /**
     * The message of no matching option.
     */
    noMatchedMsg: PropTypes.string,

    shouldPreventContainerScroll: PropTypes.bool,

    popupChildren: PropTypes.any,

    useDynamicRenderList: PropTypes.bool,
    listHeight: PropTypes.number,
    itemHeight: PropTypes.number,
    scrollBuffer: PropTypes.number,

    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    renderer: PropTypes.func,

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onItemClick: PropTypes.func,

    /**
     * Callback function fired when the popup is open.
     */
    onOpenPopup: PropTypes.func,

    /**
     * Callback function fired when the popup is close.
     */
    onClosePopup: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func,

    onWheel: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func,
    isHiddenInputFilter: PropTypes.bool,
    clearHiddenInputFilterInterval: PropTypes.number

};

DropdownSelect.defaultProps = {

    theme: Theme.DEFAULT,
    popupTheme: Theme.DEFAULT,

    placeholder: 'Please select ...',
    rightIconCls: 'fas fa-angle-down',
    data: [],
    disabled: false,
    selectMode: SelectMode.SINGLE_SELECT,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    autoClose: true,
    useFilter: false,
    filterIconCls: 'fas fa-search',
    useSelectAll: false,
    selectAllText: 'Select All',
    shouldPreventContainerScroll: true,
    useDynamicRenderList: false,
    isHiddenInputFilter: false,
    clearHiddenInputFilterInterval: 1000

};

export default DropdownSelect;<|endoftext|>