whole_func_string: function $SnifferProvider() {
    this.$get = ['$window', '$document', function($window, $document) {
      var eventSupport = {},
          android =
            int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
          boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
          document = $document[0] || {},
          documentMode = document.documentMode,
          vendorPrefix,
          vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
          bodyStyle = document.body && document.body.style,
          transitions = false,
          animations = false,
          match;
  
      if (bodyStyle) {
        for(var prop in bodyStyle) {
          if(match = vendorRegex.exec(prop)) {
            vendorPrefix = match[0];
            vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
            break;
          }
        }
  
        if(!vendorPrefix) {
          vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
        }
  
        transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
        animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
  
        if (android && (!transitions||!animations)) {
          transitions = isString(document.body.style.webkitTransition);
          animations = isString(document.body.style.webkitAnimation);
        }
      }
  
  
      return {
        // Android has history.pushState, but it does not update location correctly
        // so let's not use the history API at all.
        // http://code.google.com/p/android/issues/detail?id=17471
        // https://github.com/angular/angular.js/issues/904
  
        // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
        // so let's not use the history API also
        // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
        // jshint -W018
        history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
        // jshint +W018
        hashchange: 'onhashchange' in $window &&
                    // IE8 compatible mode lies
                    (!documentMode || documentMode > 7),
        hasEvent: function(event) {
          // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
          // it. In particular the event is not fired when backspace or delete key are pressed or
          // when cut operation is performed.
          if (event == 'input' && msie == 9) return false;
  
          if (isUndefined(eventSupport[event])) {
            var divElm = document.createElement('div');
            eventSupport[event] = 'on' + event in divElm;
          }
  
          return eventSupport[event];
        },
        csp: csp(),
        vendorPrefix: vendorPrefix,
        transitions : transitions,
        animations : animations,
        android: android,
        msie : msie,
        msieDocumentMode: documentMode
      };
    }];
  }
func_code_string: function $SnifferProvider() {
    this.$get = ['$window', '$document', function($window, $document) {
      var eventSupport = {},
          android =
            int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
          boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
          document = $document[0] || {},
          documentMode = document.documentMode,
          vendorPrefix,
          vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
          bodyStyle = document.body && document.body.style,
          transitions = false,
          animations = false,
          match;
  
      if (bodyStyle) {
        for(var prop in bodyStyle) {
          if(match = vendorRegex.exec(prop)) {
            vendorPrefix = match[0];
            vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
            break;
          }
        }
  
        if(!vendorPrefix) {
          vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
        }
  
        transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
        animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
  
        if (android && (!transitions||!animations)) {
          transitions = isString(document.body.style.webkitTransition);
          animations = isString(document.body.style.webkitAnimation);
        }
      }
  
  
      return {
        // Android has history.pushState, but it does not update location correctly
        // so let's not use the history API at all.
        // http://code.google.com/p/android/issues/detail?id=17471
        // https://github.com/angular/angular.js/issues/904
  
        // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
        // so let's not use the history API also
        // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
        // jshint -W018
        history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
        // jshint +W018
        hashchange: 'onhashchange' in $window &&
                    // IE8 compatible mode lies
                    (!documentMode || documentMode > 7),
        hasEvent: function(event) {
          // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
          // it. In particular the event is not fired when backspace or delete key are pressed or
          // when cut operation is performed.
          if (event == 'input' && msie == 9) return false;
  
          if (isUndefined(eventSupport[event])) {
            var divElm = document.createElement('div');
            eventSupport[event] = 'on' + event in divElm;
          }
  
          return eventSupport[event];
        },
        csp: csp(),
        vendorPrefix: vendorPrefix,
        transitions : transitions,
        animations : animations,
        android: android,
        msie : msie,
        msieDocumentMode: documentMode
      };
    }];
  }
func_documentation_string: !!! This is an undocumented "private" service !!!
  
  @name $sniffer
  @requires $window
  @requires $document
  
  @property {boolean} history Does the browser support html5 history api ?
  @property {boolean} hashchange Does the browser support hashchange event ?
  @property {boolean} transitions Does the browser support CSS transition events ?
  @property {boolean} animations Does the browser support CSS animation events ?
  
  @description
  This is very simple implementation of testing browser's features.