@@ -63,7 +63,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
6363  ctrl . select  =  select ; 
6464  ctrl . listEnter  =  onListEnter ; 
6565  ctrl . listLeave  =  onListLeave ; 
66-   ctrl . mouseUp  =  onMouseup ; 
66+   ctrl . focusInput  =  focusInputElement ; 
6767  ctrl . getCurrentDisplayValue  =  getCurrentDisplayValue ; 
6868  ctrl . registerSelectedItemWatcher  =  registerSelectedItemWatcher ; 
6969  ctrl . unregisterSelectedItemWatcher  =  unregisterSelectedItemWatcher ; 
@@ -103,6 +103,10 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
103103      gatherElements ( ) ; 
104104      moveDropdown ( ) ; 
105105
106+       // Touch devices often do not send a click event on tap. We still want to focus the input 
107+       // and open the options pop-up in these cases. 
108+       $element . on ( 'touchstart' ,  focusInputElement ) ; 
109+ 
106110      // Forward all focus events to the input element when autofocus is enabled 
107111      if  ( $scope . autofocus )  { 
108112        $element . on ( 'focus' ,  focusInputElement ) ; 
@@ -366,12 +370,31 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
366370
367371  // event/change handlers 
368372
373+   /** 
374+    * @param  {Event } $event 
375+    */ 
376+   function  preventDefault ( $event )  { 
377+     $event . preventDefault ( ) ; 
378+   } 
379+ 
380+   /** 
381+    * @param  {Event } $event 
382+    */ 
383+   function  stopPropagation ( $event )  { 
384+     $event . stopPropagation ( ) ; 
385+   } 
386+ 
369387  /** 
370388   * Handles changes to the `hidden` property. 
371-    * @param  {boolean } hidden 
372-    * @param  {boolean } oldHidden 
389+    * @param  {boolean } hidden true to hide the options pop-up, false to show it.  
390+    * @param  {boolean } oldHidden the previous value of hidden  
373391   */ 
374392  function  handleHiddenChange  ( hidden ,  oldHidden )  { 
393+     var  scrollContainerElement ; 
394+ 
395+     if  ( elements )  { 
396+       scrollContainerElement  =  angular . element ( elements . scrollContainer ) ; 
397+     } 
375398    if  ( ! hidden  &&  oldHidden )  { 
376399      positionDropdown ( ) ; 
377400
@@ -380,13 +403,23 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
380403      reportMessages ( true ,  ReportType . Count  |  ReportType . Selected ) ; 
381404
382405      if  ( elements )  { 
383-         $mdUtil . disableScrollAround ( elements . ul ) ; 
384-         enableWrapScroll  =  disableElementScrollEvents ( angular . element ( elements . wrap ) ) ; 
385-         ctrl . documentElement . on ( 'click' ,  handleClickOutside ) ; 
406+         $mdUtil . disableScrollAround ( elements . scrollContainer ) ; 
407+         enableWrapScroll  =  disableElementScrollEvents ( elements . wrap ) ; 
408+         if  ( $mdUtil . isIos )  { 
409+           ctrl . documentElement . on ( 'touchend' ,  handleTouchOutsidePanel ) ; 
410+           if  ( scrollContainerElement )  { 
411+             scrollContainerElement . on ( 'touchstart touchmove touchend' ,  stopPropagation ) ; 
412+           } 
413+         } 
386414        $mdUtil . nextTick ( updateActiveOption ) ; 
387415      } 
388416    }  else  if  ( hidden  &&  ! oldHidden )  { 
389-       ctrl . documentElement . off ( 'click' ,  handleClickOutside ) ; 
417+       if  ( $mdUtil . isIos )  { 
418+         ctrl . documentElement . off ( 'touchend' ,  handleTouchOutsidePanel ) ; 
419+         if  ( scrollContainerElement )  { 
420+           scrollContainerElement . off ( 'touchstart touchmove touchend' ,  stopPropagation ) ; 
421+         } 
422+       } 
390423      $mdUtil . enableScrolling ( ) ; 
391424
392425      if  ( enableWrapScroll )  { 
@@ -397,29 +430,27 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
397430  } 
398431
399432  /** 
400-    * Handling click  events that bubble up to the document is required for closing the dropdown 
401-    * panel on click  outside of the panel on iOS. 
433+    * Handling touch  events that bubble up to the document is required for closing the dropdown 
434+    * panel on touch  outside of the options pop-up  panel on iOS. 
402435   * @param  {Event } $event 
403436   */ 
404-   function  handleClickOutside ( $event )  { 
437+   function  handleTouchOutsidePanel ( $event )  { 
405438    ctrl . hidden  =  true ; 
439+     // iOS does not blur the pop-up for touches on the scroll mask, so we have to do it. 
440+     doBlur ( true ) ; 
406441  } 
407442
408443  /** 
409-    * Disables scrolling for a specific element 
444+    * Disables scrolling for a specific element. 
445+    * @param  {!string|!DOMElement } element to disable scrolling 
446+    * @return  {Function } function to call to re-enable scrolling for the element 
410447   */ 
411448  function  disableElementScrollEvents ( element )  { 
412- 
413-     function  preventDefault ( e )  { 
414-       e . preventDefault ( ) ; 
415-     } 
416- 
417-     element . on ( 'wheel' ,  preventDefault ) ; 
418-     element . on ( 'touchmove' ,  preventDefault ) ; 
449+     var  elementToDisable  =  angular . element ( element ) ; 
450+     elementToDisable . on ( 'wheel touchmove' ,  preventDefault ) ; 
419451
420452    return  function ( )  { 
421-       element . off ( 'wheel' ,  preventDefault ) ; 
422-       element . off ( 'touchmove' ,  preventDefault ) ; 
453+       elementToDisable . off ( 'wheel touchmove' ,  preventDefault ) ; 
423454    } ; 
424455  } 
425456
@@ -439,13 +470,6 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
439470    ctrl . hidden  =  shouldHide ( ) ; 
440471  } 
441472
442-   /** 
443-    * When the mouse button is released, send focus back to the input field. 
444-    */ 
445-   function  onMouseup  ( )  { 
446-     elements . input . focus ( ) ; 
447-   } 
448- 
449473  /** 
450474   * Handles changes to the selected item. 
451475   * @param  selectedItem 
@@ -673,7 +697,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
673697
674698  /** 
675699   * Returns the display value for an item. 
676-    * @param  item 
700+    * @param  { * }  item 
677701   * @returns  {* } 
678702   */ 
679703  function  getDisplayValue  ( item )  { 
@@ -689,7 +713,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
689713    /** 
690714     * Getter function to invoke user-defined expression (in the directive) 
691715     * to convert your object to a single string. 
692-      * @param  item 
716+      * @param  { * }  item 
693717     * @returns  {string|null } 
694718     */ 
695719    function  getItemText  ( item )  { 
@@ -699,7 +723,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
699723
700724  /** 
701725   * Returns the locals object for compiling item templates. 
702-    * @param  item 
726+    * @param  { * }  item 
703727   * @returns  {Object|undefined } 
704728   */ 
705729  function  getItemAsNameVal  ( item )  { 
@@ -837,14 +861,14 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
837861   * Defines a public property with a handler and a default value. 
838862   * @param  {string } key 
839863   * @param  {Function } handler function 
840-    * @param  {* } value  default value 
864+    * @param  {* } defaultValue  default value 
841865   */ 
842-   function  defineProperty  ( key ,  handler ,  value )  { 
866+   function  defineProperty  ( key ,  handler ,  defaultValue )  { 
843867    Object . defineProperty ( ctrl ,  key ,  { 
844-       get : function  ( )  {  return  value ;  } , 
868+       get : function  ( )  {  return  defaultValue ;  } , 
845869      set : function  ( newValue )  { 
846-         var  oldValue  =  value ; 
847-         value         =  newValue ; 
870+         var  oldValue  =  defaultValue ; 
871+         defaultValue         =  newValue ; 
848872        handler ( newValue ,  oldValue ) ; 
849873      } 
850874    } ) ; 
@@ -1014,7 +1038,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
10141038  function  updateVirtualScroll ( )  { 
10151039    // elements in virtual scroll have consistent heights 
10161040    var  optionHeight  =  elements . li [ 0 ] . offsetHeight , 
1017-         top  =  optionHeight  *  ctrl . index , 
1041+         top  =  optionHeight  *  Math . max ( 0 ,   ctrl . index ) , 
10181042        bottom  =  top  +  optionHeight , 
10191043        containerHeight  =  elements . scroller . clientHeight , 
10201044        scrollTop  =  elements . scroller . scrollTop ; 
@@ -1028,7 +1052,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
10281052
10291053  function  updateStandardScroll ( )  { 
10301054    // elements in standard scroll have variable heights 
1031-     var  selected  =   elements . li [ ctrl . index ]   ||   elements . li [ 0 ] ; 
1055+     var  selected  =   elements . li [ Math . max ( 0 ,   ctrl . index ) ] ; 
10321056    var  containerHeight  =  elements . scrollContainer . offsetHeight , 
10331057        top  =  selected  &&  selected . offsetTop  ||  0 , 
10341058        bottom  =  top  +  selected . clientHeight , 
0 commit comments