text: 
  /*
  
  Physical Device Testing Scenarios
  ---------------------------------
  - Keyboard should show up when tapping on a text input
  - Keyboard should show up when tapping on a label which surrounds a text input
  - Keyboard should stay up if focused text input is tapped again
  - Keyboard should go away when text input is focused, then tapped outside of input
  - Keyboard should hide when tapping the virtual keyboard's "Done" or down arrow, but tapping
  - Should be able to move an inputs text caret when its focused
  - Should be able to move an inputs text caret when its focused, and is wrapped with a label
    the input again will bring up the keyboard again
  - Options dialog should show when tapping on a select
  - Options dialog should show when tapping on a label which surrounds a select (not working in Android 2.3)
  - Tapping a button element should fire one click
  - Tapping an anchor element should fire one click
  - Tapping a checkbox should fire one click
  - Tapping a label which surrounds a checkbox should fire one click
  - Tapping a radio button should fire one click
  - Tapping a label which surrounds a radio button should fire one click
  - Moving an input[range] slider should work
  - Moving an input[range] slider when its in menu content of a should work
  - Tapping the track on an input[range] slider should move the knob to that location (not a default in iOS)
  - Tapping an input[file] should bring up the file dialog
  - After tapping an input[file] and closing the input file dialog, tap a different
    element and the file dialog should NOT show up
  - Element which is disabled should not be clicked
  - Holding a touchstart, and not moving, should fire the click no matter how long the hold
  - Holding a mousedown, and not moving, should fire the click no matter how long the hold
  - Holding touchstart, then moving a few pixels cancels the click
  - Holding mousedown, then moving a few pixels cancels the click
  - Touchstart should set and remove the activated css class
  - Mousedown should set and remove the activated css class
  - Holding touchstart, then moving a few pixels removes the activated css class
  - Holding mousedown, then moving a few pixels removes the activated css class
  - An element or one of its parents with data-tap-disabled attribute should still click, but w/ a delay
  - ALL THE ABOVE, BUT NOW WITH NG-CLICK ON THE INPUT!
  - Tapping a div with an click event added should fire one click
  - Tapping an img with an click event added should fire one click
  - Can scroll when target is a text input, but it does not have focus
  - Can scroll when the target is a text input and it already has focus
  - Can hold a text input and move its text caret/cursor
  - Can scroll when the target is a label
  - Does not change focus 300ms after when tapping an input and the keyboard shows up
  - The blinking cursor says in the input 300ms after the tap
  - Can hit the keyboard's "next" button to change focus to the next input
  - When flicking down a page, and a target was an input, it shouldn't focus and jank scrolling
  - Do not show text caret of a focused input while scrolling, no matter what the target is
  - After scrolling has come to a halt, previously focused input should be focused again
  - Keyboard should stay up while scrolling if an input was focused
  - Keyboard should not go away after scrolling stops and it focuses back on the previous focused input
  - Should not create clones for tap inputs to hide cursor, like for checkboxes, radio, range, select
  - Focus on an input, flick up, let go, and during animation flick down, and clone should go away
  - While inputs are actively scrolling you should not be able to change focus
  - If you touchstart on a text input, then scrolland touchend, it should not bring up the keyboard
  - If you touchstart on a label wrapping a text input, then scroll and touchend, it should not bring up the keyboard
  - When focused in a text input, be able to get out when tapping a checkbox
  - Can scroll when the target is a select element (touch events only)
  - Can scroll when the target is a label which wraps select element
  - Can open the select options on touch when not scrolling
  - Can open the select options mouse click when target is a select
  - Can open the select options when target is a label wrapping a select
  
  Tested on:
  ----------------------------
  - iOS 6.1 iPad 3
  - iOS 7.0 iPhone 4
  - iOS 7.0 iPhone 5
  - iOS 7.1 iPhone 5
  - iOS 7.1 Simulator
  - Android 2.3 HTC Incredible
  - Android 2.3 Samsung Galaxy S
  - Android 4.0 HTC Incredible
  - Android 4.2 Nexus 4
  - Android 4.3 Samsung S3
  - Android 4.4 Motorola Moto G
  - Android 4.4 Nexus 5
  - OSX Chrome
  - OSX Chrome (emulated touch screen)
  - OSX Firefox
  
  */
  
  window.console.log = function(){};
  
  describe('Ionic Tap', function() {
    var deregisterTap;
  
    beforeEach(function() {
      window._setTimeout = window.setTimeout;
      window.setTimeout = function(){};
      _activeElement = null; // the element which has focus
  
      deregisterTap = ionic.tap.register(document.createElement('div'));
      ionic.scroll = { isScrolling: false };
    });
  
    afterEach(function(){
      window.setTimeout = window._setTimeout;
      deregisterTap();
    });
  
    it('Should trigger a labels child inputs click, and should not stop the labels end event so Android allows text editing', function() {
      var e = {
        type: 'touchstart',
        target: {
          tagName: 'LABEL',
          dispatchEvent: function() { e.target.dispatchedEvent = true; },
          focus: function() { e.target.focused = true; },
          control: {
            tagName: 'INPUT',
            dispatchEvent: function() { e.target.control.dispatchedEvent = true; },
            focus: function() { e.target.control.focused = true; }
          }
        },
        stopPropagation: function() { e.stoppedPropagation = true; },
        preventDefault: function() { e.preventedDefault = true; }
      };
  
      tapClick(e);
  
      expect( e.target.dispatchedEvent ).toBeUndefined();
      expect( e.target.focused ).toBeUndefined();
  
      expect( e.target.control.dispatchedEvent ).toBeDefined();
      expect( e.target.control.focused ).toBeDefined();
  
      expect( e.stoppedPropagation ).toBeUndefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should trigger a click for an element w/out a wrapping label', function() {
      var e = {
        type: 'touchstart',
        target: {
          tagName: 'INPUT',
          dispatchEvent: function() { e.target.dispatchedEvent = true; },
          focus: function() { e.target.focused = true; }
        },
        stopPropagation: function() { e.stoppedPropagation = true; },
        preventDefault: function() { e.preventedDefault = true; }
      };
  
      tapClick(e);
  
      expect( e.target.dispatchedEvent ).toBeDefined();
      expect( e.target.focused ).toBeDefined();
  
      expect( e.stoppedPropagation ).toBeUndefined();
      expect( e.preventedDefault ).toBeUndefined();
  
    });
  
    it('Should not trigger a click if tapPointerMoved has moved', function() {
      var e = {
        type: 'touchstart',
        target: {
          tagName: 'INPUT',
          dispatchEvent: function() { e.target.dispatchedEvent = true; },
          focus: function() { e.target.focused = true; }
        },
        stopPropagation: function() { e.stoppedPropagation = true; },
        preventDefault: function() { e.preventedDefault = true; }
      };
  
      tapPointerMoved = true;
      expect( tapClick(e) ).toEqual(false);
  
    });
  
    it('Should trigger click on mouseup and when nearby mousedown happened', function() {
      var e = {
        type: 'mousedown',
        clientX: 100, clientY: 100,
        target: {
          tagName: 'INPUT',
          dispatchEvent: function() { e.target.dispatchedEvent = true; },
          focus: function() { e.target.focused = true; },
          classList: { contains: function(){} }
        },
        stopPropagation: function() { e.stoppedPropagation = true; },
        preventDefault: function() { e.preventedDefault = true; }
      };
  
      expect( e.target.dispatchedEvent ).toBeUndefined();
  
      tapMouseDown({clientX: 101, clientY: 101});
      tapMouseUp(e);
  
      expect( e.target.dispatchedEvent ).toBeDefined();
    });
  
    it('Should not trigger click on mouseup because mousedown coordinates too far away', function() {
      var e = {
        clientX: 100, clientY: 100,
        target: {
          tagName: 'INPUT',
          dispatchEvent: function() { e.target.dispatchedEvent = true; },
          focus: function() { e.target.focused = true; },
          classList: { contains: function(){} }
        },
        stopPropagation: function() { e.stoppedPropagation = true; },
        preventDefault: function() { e.preventedDefault = true; }
      };
  
      expect( e.target.dispatchedEvent ).toBeUndefined();
  
      tapMouseDown({clientX: 201, clientY: 101});
      tapMouseUp(e);
  
      expect( e.target.dispatchedEvent ).toBeUndefined();
    });
  
    it('Should set tapHasPointerMoved=false on tapTouchStart', function() {
      tapPointerMoved = null;
      tapTouchStart({ preventDefault:function(){} });
      expect( tapPointerMoved ).toEqual(false);
    });
  
    it('Should not preventDefault on text input that already has focus and is iOS', function() {
      // prevent the default so iOS doesn't auto scroll to the input
      ionic.Platform.setPlatform('ios');
      var label = document.createElement('label');
      var textarea = document.createElement('textarea');
      label.appendChild(textarea);
      var e = {
        target: label,
        preventDefault: function() { e.preventedDefault = true; }
      };
  
      tapActiveEle = textarea;
  
      tapTouchStart(e);
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should preventDefault on text input that does not have focus and is iOS', function() {
      // prevent the default so iOS doesn't auto scroll to the input
      ionic.Platform.setPlatform('ios');
      var label = document.createElement('label');
      var textarea = document.createElement('textarea');
      label.appendChild(textarea);
      var e = {
        target: label,
        preventDefault: function() { e.preventedDefault = true; }
      };
  
      tapTouchStart(e);
      expect( e.preventedDefault ).toEqual(true);
    });
  
    it('Should not preventDefault on text input target thats not iOS', function() {
      // do not prevent default on touchend of a text input or else you cannot move the text caret
      ionic.Platform.setPlatform('android');
      var label = document.createElement('label');
      var textarea = document.createElement('textarea');
      label.appendChild(textarea);
      var e = {
        target: label,
        preventDefault: function() { e.preventedDefault = true; }
      };
      tapTouchStart(e);
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should set tapPointerMoved=false on tapTouchCancel', function() {
      tapPointerMoved = true;
      tapTouchCancel();
      expect( tapPointerMoved ).toEqual(false);
    });
  
    it('Should set tapHasPointerMoved=true on tapTouchMove', function() {
      tapPointerMoved = null;
      tapTouchStart({ clientX: 100, clientY: 100, preventDefault:function(){} });
      expect( tapPointerMoved ).toEqual(false);
      tapTouchMove({ clientX: 200, clientY: 100, target: document.createElement('button') });
      expect( tapPointerMoved ).toEqual(true);
    });
  
    it('Should set tapHasPointerMoved=false on tapMouseDown', function() {
      tapPointerMoved = null;
      tapMouseDown({});
      expect( tapPointerMoved ).toEqual(false);
    });
  
    it('Should set tapPointerMoved=false on tapMouseUp', function() {
      tapPointerMoved = true;
      tapMouseUp({ target: {} });
      expect( tapPointerMoved ).toEqual(false);
    });
  
    it('Should set tapHasPointerMoved=true on tapMouseMove', function() {
      tapPointerMoved = null;
      tapMouseDown({ clientX: 100, clientY: 100 });
      expect( tapPointerMoved ).toEqual(false);
      tapMouseMove({ clientX: 200, clientY: 100, target: document.createElement('button') });
      expect( tapPointerMoved ).toEqual(true);
    });
  
    it('Should stop event on mouseup if touch is enabled', function() {
      tapEnabledTouchEvents = true;
      var e = {
        stopPropagation: function() { this.stoppedPropagation = true; },
        preventDefault: function() { this.preventedDefault = true; }
      };
      tapMouseUp(e);
      expect( e.stoppedPropagation ).toEqual(true);
      expect( e.preventedDefault ).toEqual(true);
    });
  
    it('Should not stop event on mouseup if the target was a select element', function() {
      tapEnabledTouchEvents = false;
      e = {
        target: document.createElement('select'),
        stopPropagation: function() { this.stoppedPropagation = true; },
        preventDefault: function() { this.preventedDefault = true; }
      };
      expect( tapMouseUp(e) ).toBeFalsy();
      expect( e.stoppedPropagation ).toBeUndefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should not stop event on mouseup if the target was an option element', function() {
      tapEnabledTouchEvents = false;
      e = {
        target: document.createElement('option'),
        stopPropagation: function() { this.stoppedPropagation = true; },
        preventDefault: function() { this.preventedDefault = true; }
      };
      expect( tapMouseUp(e) ).toEqual(false);
      expect( e.stoppedPropagation ).toBeUndefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should stop event on mouseup if the target was an ion-option-button element', function() {
      tapEnabledTouchEvents = false;
      e = {
        target: document.createElement('ion-option-button'),
        stopPropagation: function() { this.stoppedPropagation = true; },
        preventDefault: function() { this.preventedDefault = true; }
      };
      expect( tapMouseUp(e) ).toBeUndefined();
      expect( e.stoppedPropagation ).toBeUndefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should not stop event on mouseup if touch is not enabled', function() {
      tapEnabledTouchEvents = false;
      e = {
        target: document.createElement('button'),
        stopPropagation: function() { this.stoppedPropagation = true; },
        preventDefault: function() { this.preventedDefault = true; }
      };
      tapMouseUp(e);
      expect( e.stoppedPropagation ).toBeUndefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should trigger click on touchend and nearby touchstart happened', function() {
      var e = {
        type: 'touchend',
        clientX: 101, clientY: 101,
        target: {
          tagName: 'INPUT',
          dispatchEvent: function() { e.target.dispatchedEvent = true; },
          focus: function() { e.target.focused = true; },
          classList: { contains:function(){}}
        },
        stopPropagation: function() { e.stoppedPropagation = true; },
        preventDefault: function() { e.preventedDefault = true; }
      };
  
      tapTouchStart({clientX: 100, clientY: 100, preventDefault:function(){}});
      tapTouchEnd(e);
  
      expect( e.target.dispatchedEvent ).toBeDefined();
    });
  
    it('Should not trigger click on touchend because touchstart coordinates too far away', function() {
      var e = {
        type: 'touchstart',
        clientX: 100, clientY: 100,
        target: {
          tagName: 'INPUT',
          dispatchEvent: function() { e.target.dispatchedEvent = true; },
          focus: function() { e.target.focused = true; },
          classList: { contains:function(){}}
        },
        stopPropagation: function() { e.stoppedPropagation = true; },
        preventDefault: function() { e.preventedDefault = true; }
      };
  
      expect( e.target.dispatchedEvent ).toBeUndefined();
  
      tapTouchStart({clientX: 200, clientY: 100, preventDefault:function(){}});
  
      tapTouchEnd(e);
  
      expect( e.target.dispatchedEvent ).toBeUndefined();
    });
  
    it('Should tapEnabledTouchEvents because of touchstart', function() {
      tapEnabledTouchEvents = false;
      tapTouchStart({preventDefault:function(){}});
      tapEnabledTouchEvents = true;
    });
  
    it('Should cancel click on touchcancel', function() {
      tapTouchCancel();
      expect(tapPointerMoved).toEqual(false);
    });
  
    it('Should cancel click when touchmove coordinates goes too far from touchstart coordinates', function() {
      var e = { clientX: 100, clientY: 100, preventDefault:function(){} };
      tapTouchStart(e);
  
      expect( tapTouchMove({ clientX: 102, clientY: 100, target: document.createElement('button') }) ).toBeUndefined();
  
      expect( tapTouchMove({ clientX: 105, clientY: 100, target: document.createElement('button') }) ).toBeUndefined();
  
      expect( tapTouchMove({ clientX: 200, clientY: 100, target: document.createElement('button') }) ).toEqual(false);
    });
  
    it('Should cancel click when touchend coordinates are too far from touchstart coordinates', function() {
      var e = {
        clientX: 100, clientY: 100,
        dispatchEvent: function(){ this.dispatchedEvent = true; },
        preventDefault:function(){},
        classList: { contains:function(){}}
      };
      tapTouchStart(e);
      tapTouchEnd({ clientX: 200, clientY: 100, target: document.createElement('button') });
      expect( e.dispatchedEvent ).toBeUndefined();
    });
  
    it('Should preventDefault on touchend when the target is a select', function() {
      var e = {
        target: document.createElement('select'),
        clientX: 100, clientY: 100,
        dispatchEvent: function(){ this.dispatchedEvent = true; },
        preventDefault:function(){ this.preventedDefault = true; }
      };
      tapTouchEnd(e);
      expect( e.preventedDefault ).toEqual(true);
  
      e = {
        target: document.createElement('div'),
        clientX: 100, clientY: 100,
        dispatchEvent: function(){ this.dispatchedEvent = true; },
        preventDefault:function(){ this.preventedDefault = true; }
      };
      tapTouchEnd(e);
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should cancel click when mousemove coordinates goes too far from mousedown coordinates', function() {
      var e = { clientX: 100, clientY: 100 };
      tapMouseDown(e);
  
      expect( tapMouseMove({ clientX: 102, clientY: 100, target: document.createElement('button') }) ).toBeUndefined();
  
      expect( tapMouseMove({ clientX: 105, clientY: 100, target: document.createElement('button') }) ).toBeUndefined();
  
      expect( tapMouseMove({ clientX: 200, clientY: 100, target: document.createElement('button') }) ).toEqual(false);
    });
  
    it('Should cancel click when mouseup coordinates are too far from mousedown coordinates', function() {
      var e1 = {
        target: document.createElement('button'),
        clientX: 100, clientY: 100,
        dispatchEvent: function(){ this.dispatchedEvent = true; }
      };
      tapMouseDown(e1);
  
      var e2 = {
        target: document.createElement('button'),
        clientX: 100, clientY: 100,
        dispatchEvent: function(){ this.dispatchedEvent = true; }
      };
      tapMouseUp(e2);
      expect( e2.dispatchedEvent ).toBeUndefined();
    });
  
    it('Should do nothing if mousedown is a custom event from ionic tap', function() {
      var e = {
        isTapHandled: false,
        isIonicTap: true
      };
      tapMouseDown(e);
      expect( e.isTapHandled ).toEqual(false);
    });
  
    it('Should preventDefault on mousedown if touchend target is different than mousedown target', function() {
      tapLastTouchTarget = null;
  
      var touchEndEvent = {
        target: document.createElement('button'),
        clientX: 100, clientY: 100,
        preventDefault: function(){ this.defaultedPrevented = true; }
      };
      tapTouchEnd(touchEndEvent);
      expect( tapLastTouchTarget ).toEqual(touchEndEvent.target);
  
      var mouseDownEvent = {
        target: document.createElement('textarea'),
        clientX: 100, clientY: 100,
        preventDefault: function(){ this.defaultedPrevented = true; },
        stopPropagation: function(){ this.stoppedPropagation = true; }
      };
      tapMouseDown(mouseDownEvent);
      expect( mouseDownEvent.defaultedPrevented ).toEqual(true);
    });
  
    it('Should not preventDefault on mousedown if the target is a select element', function() {
      tapEnabledTouchEvents = true;
      var e = {
        isTapHandled: false,
        isIonicTap: false,
        target: document.createElement('select'),
        preventDefault: function(){ this.defaultedPrevented = true; },
        stopPropagation: function(){ this.stoppedPropagation = true; }
      };
      tapMouseDown(e);
      expect( e.stoppedPropagation ).toEqual(true);
      expect( e.defaultedPrevented ).toBeUndefined();
  
      e = {
        isTapHandled: false,
        isIonicTap: false,
        target: document.createElement('option'),
        preventDefault: function(){ this.defaultedPrevented = true; },
        stopPropagation: function(){ this.stoppedPropagation = true; }
      };
      tapMouseDown(e);
      expect( e.stoppedPropagation ).toEqual(true);
      expect( e.defaultedPrevented ).toBeUndefined();
    });
  
    it('Should tapClick with touchend and fire immediately', function() {
      var e = {
        target: {
          tagName: 'button',
          dispatchEvent: function(){
            this.dispatchedEvent = true;
          }
        }
      };
      tapClick(e);
      expect(e.target.dispatchedEvent).toEqual(true);
    });
  
    it('Should tapHasPointerMoved false if are null', function() {
      expect( tapHasPointerMoved(null) ).toEqual(false);
    });
  
    it('Should tapPointerStart false if are null', function() {
      tapPointerStart = null;
      expect( tapHasPointerMoved(null) ).toEqual(false);
    });
  
    it('Should tapPointerStart false if are null', function() {
      var e = {};
      tapPointerStart = {x:0, y:0};
      expect( tapHasPointerMoved(e) ).toEqual(false);
    });
  
    it('Should tapHasPointerMoved true if greater than or equal to release tolerance', function() {
      tapPointerStart = { x: 100, y: 100 };
  
      var s = tapHasPointerMoved({ clientX: 111, clientY: 100, target: document.createElement('button') });
      expect(s).toEqual(true);
  
      s = tapHasPointerMoved({ clientX: 89, clientY: 100, target: document.createElement('button') });
      expect(s).toEqual(true);
  
      s = tapHasPointerMoved({ clientX: 100, clientY: 109, target: document.createElement('button') });
      expect(s).toEqual(true);
  
      s = tapHasPointerMoved({ clientX: 100, clientY: 91, target: document.createElement('button') });
      expect(s).toEqual(true);
  
      s = tapHasPointerMoved({ clientX: 100, clientY: 200, target: document.createElement('button') });
      expect(s).toEqual(true);
    });
  
    it('Should tapHasPointerMoved false if less than release tolerance', function() {
      tapPointerStart = { x: 100, y: 100 };
  
      var s = tapHasPointerMoved({ clientX: 100, clientY: 100, target: document.createElement('button') });
      expect(s).toEqual(false);
  
      s = tapHasPointerMoved({ clientX: 104, clientY: 100, target: document.createElement('button') });
      expect(s).toEqual(false);
  
      s = tapHasPointerMoved({ clientX: 96, clientY: 100, target: document.createElement('button') });
      expect(s).toEqual(false);
  
      s = tapHasPointerMoved({ clientX: 100, clientY: 102, target: document.createElement('button') });
      expect(s).toEqual(false);
  
      s = tapHasPointerMoved({ clientX: 100, clientY: 98, target: document.createElement('button') });
      expect(s).toEqual(false);
    });
  
    it('Should not be tapHasPointerMoved if 0 coordinates', function() {
      var e = { clientX: 0, clientY: 0 };
      var s = tapHasPointerMoved(e, { clientX: 100, clientY: 100 });
      expect(s).toEqual(false);
    });
  
    it('Should get coordinates from page mouse event', function() {
      var e = { pageX: 77, pageY: 77 };
      var c = getPointerCoordinates(e);
      expect(c).toEqual({x:77, y: 77});
    });
  
    it('Should get coordinates from client mouse event', function() {
      var e = { clientX: 77, clientY: 77 };
      var c = getPointerCoordinates(e);
      expect(c).toEqual({x:77, y: 77});
    });
  
    it('Should get coordinates from changedTouches touches', function() {
      var e = {
        touches: [{ clientX: 99, clientY: 99 }],
        changedTouches: [{ clientX: 88, clientY: 88 }]
      };
      var c = getPointerCoordinates(e);
      expect(c).toEqual({x:88, y: 88});
    });
  
    it('Should get coordinates from page touches', function() {
      var e = {
        touches: [{ pageX: 99, pageY: 99 }]
      };
      var c = getPointerCoordinates(e);
      expect(c).toEqual({x:99, y: 99});
    });
  
    it('Should get coordinates from client touches', function() {
      var e = {
        touches: [{ clientX: 99, clientY: 99 }]
      };
      var c = getPointerCoordinates(e);
      expect(c).toEqual({x:99, y: 99});
    });
  
    it('Should get 0 coordinates', function() {
      var e = {};
      var c = getPointerCoordinates(e);
      expect(c).toEqual({x:0, y: 0});
    });
  
    it('Should not tapClick for disabled elements', function() {
      // Disabled elements should not be tapped
      var targetEle = document.createElement('input');
      targetEle.disabled = true;
  
      var e = {
        target: targetEle
      };
  
      expect( tapClick(e) ).toEqual(false);
    });
  
    it('Should ionic.tap.requiresNativeClick for invalid element', function() {
      expect( ionic.tap.requiresNativeClick( null ) ).toEqual(true);
    });
  
    it('Should ionic.tap.requiresNativeClick for input.disabled', function() {
      var ele = document.createElement('input');
      ele.disabled = true;
      expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
    });
  
    it('Should ionic.tap.requiresNativeClick for input[range]', function() {
      var ele = document.createElement('input');
      ele.type = 'range';
      expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
    });
  
    it('Should ionic.tap.requiresNativeClick for input[file]', function() {
      var ele = document.createElement('input');
      ele.type = 'file';
      expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
    });
  
    it('Should ionic.tap.requiresNativeClick for video element', function() {
      var ele = document.createElement('video');
      expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
    });
  
    it('Should ionic.tap.requiresNativeClick for object element', function() {
      var ele = document.createElement('object');
      expect( ionic.tap.requiresNativeClick( ele ) ).toEqual(true);
    });
  
    it('Should not ionic.tap.requiresNativeClick for common inputs', function() {
      var inputTypes = ['text', 'email', 'search', 'tel', 'number', 'date', 'month', 'password', null, undefined, ''];
      for(var x=0; x<inputTypes.length; x++) {
        var targetEle = document.createElement('input');
        targetEle.type = inputTypes[x];
        expect( ionic.tap.requiresNativeClick(targetEle) ).toEqual(false);
      }
      expect( ionic.tap.requiresNativeClick( document.createElement('img') ) ).toEqual(false);
      expect( ionic.tap.requiresNativeClick( document.createElement('div') ) ).toEqual(false);
      expect( ionic.tap.requiresNativeClick( document.createElement('textarea') ) ).toEqual(false);
      expect( ionic.tap.requiresNativeClick( document.createElement('select') ) ).toEqual(false);
    });
  
    it('Should ionic.tap.requiresNativeClick for an element with data-tap-disabled attribute', function() {
      var div = document.createElement('div');
      expect( ionic.tap.requiresNativeClick( div ) ).toEqual(false);
  
      div.setAttribute('data-tap-disabled', "true");
      expect( ionic.tap.requiresNativeClick( div ) ).toEqual(true);
    });
  
    it('Should ionic.tap.requiresNativeClick for an element with one of its parents with data-tap-disabled attribute', function() {
      var div1 = document.createElement('div');
      var div2 = document.createElement('div');
      var div3 = document.createElement('div');
      var div4 = document.createElement('div');
      var div5 = document.createElement('div');
  
      div1.appendChild(div2);
      div2.appendChild(div3);
      div3.appendChild(div4);
      div4.appendChild(div5);
  
      div2.setAttribute('data-tap-disabled', "true");
  
      expect( ionic.tap.requiresNativeClick( div1 ) ).toEqual(false);
      expect( ionic.tap.requiresNativeClick( div2 ) ).toEqual(true);
      expect( ionic.tap.requiresNativeClick( div3 ) ).toEqual(true);
      expect( ionic.tap.requiresNativeClick( div4 ) ).toEqual(true);
      expect( ionic.tap.requiresNativeClick( div5 ) ).toEqual(true);
    });
  
    it('Should not allow a click that has an textarea target but not created by tapClick', function() {
      var e = {
        target: document.createElement('textarea'),
        stopPropagation: function(){ this.stoppedPropagation = true; },
        preventDefault: function(){ this.preventedDefault = true; }
      };
      tapClickGateKeeper(e);
  
      expect( e.stoppedPropagation ).toBeDefined();
      expect( e.preventedDefault ).toBeDefined();
    });
  
    it('Should allow a click that is a from an "Enter" or "Go" keypress submit', function() {
      var target = document.createElement('button');
      target.type = 'submit';
      var e = {
        target: target,
        detail: 0,
        stopPropagation: function(){ this.stoppedPropagation = true; },
        preventDefault: function(){ this.preventedDefault = true; }
      };
      tapClickGateKeeper(e);
  
      expect( e.stoppedPropagation ).toBeUndefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should allow a click that is a tapClick', function() {
      var e = {
        target: document.createElement('input'),
        isIonicTap: true,
        stopPropagation: function(){ this.stoppedPropagation = true; },
        preventDefault: function(){ this.preventedDefault = true; }
      };
      tapClickGateKeeper(e);
  
      expect( e.stoppedPropagation ).toBeUndefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should allow a click that is an ignore element', function() {
      var e = {
        target: document.createElement('input'),
        stopPropagation: function(){ this.stoppedPropagation = true; },
        preventDefault: function(){ this.preventedDefault = true; }
      };
      e.target.type = 'range';
      tapClickGateKeeper(e);
  
      expect( e.stoppedPropagation ).toBeUndefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should stopPropagation but not preventDefault on native label click that wraps text input', function() {
      // Android requires preventDefault to not be fired when a label wraps an input
      // so that when the label is tapped it brings up the keyboard
      var e = {
        target: document.createElement('label'),
        stopPropagation: function(){ this.stoppedPropagation = true; },
        preventDefault: function(){ this.preventedDefault = true; }
      };
  
      var input = document.createElement('textarea');
      e.target.appendChild(input);
  
      tapClickGateKeeper(e);
  
      expect( e.stoppedPropagation ).toBeDefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should stopPropagation but not preventDefault on native span click inside a label that wraps text input', function() {
      var label = document.createElement('label');
      var span = document.createElement('span');
      var textarea = document.createElement('textarea');
  
      label.appendChild(span);
      label.appendChild(textarea);
  
      var e = {
        target: span,
        stopPropagation: function(){ this.stoppedPropagation = true; },
        preventDefault: function(){ this.preventedDefault = true; }
      };
  
      tapClickGateKeeper(e);
  
      expect( e.stoppedPropagation ).toBeDefined();
      expect( e.preventedDefault ).toBeUndefined();
    });
  
    it('Should focus input', function() {
      var ele = {
        tagName: 'INPUT',
        focus: function(){ this.hasFocus=true; }
      };
      tapHandleFocus(ele);
      expect( ele.hasFocus ).toEqual(true);
    });
  
    it('Should focus textarea', function() {
      var ele = {
        tagName: 'TEXTAREA',
        focus: function(){ this.hasFocus=true; }
      };
      tapHandleFocus(ele);
      expect( ele.hasFocus ).toEqual(true);
    });
  
    it('Should set tapTouchFocusedInput if touch event and text input', function() {
      tapEnabledTouchEvents = true;
      var ele = {
        tagName: 'TEXTAREA'
      };
      tapHandleFocus(ele);
      expect( tapTouchFocusedInput ).toEqual(ele);
    });
  
    it('Should not set tapTouchFocusedInput if mouse event and text input', function() {
      tapEnabledTouchEvents = false;
      var ele = {
        tagName: 'TEXTAREA'
      };
      tapHandleFocus(ele);
      expect( tapTouchFocusedInput ).toEqual(null);
    });
  
    it('Should focus select', function() {
      var ele = {
        tagName: 'SELECT',
        focus: function(){ this.hasFocus=true; },
        dispatchEvent: function(){}
      };
      tapHandleFocus(ele);
      expect( ele.hasFocus ).toEqual(true);
      expect( tapTouchFocusedInput ).toEqual(null);
    });
  
    it('Should not focus on common elements', function() {
      var tags = ['div', 'span', 'i', 'body', 'section', 'article', 'aside', 'li', 'p', 'header', 'button', 'ion-content'];
      function setFocus() {
        this.hasFocus = true;
      }
      for(var x=0; x<tags.length; x++) {
        var ele = {
          tagName: tags[x],
          focus: setFocus
        };
        tapHandleFocus(ele);
        expect( ele.hasFocus ).toBeUndefined();
      }
    });
  
    it('Should not focus on an input that already has focus', function() {
      var ele = {
        tagName: 'INPUT',
        focus: function(){ this.hasFocus=true; }
      };
      tapHandleFocus(ele);
      expect( ele.hasFocus ).toEqual(true);
  
      ele.focus = function(){ this.hasSecondFocus=true; };
      tapHandleFocus(ele);
      expect( ele.hasSecondFocus ).toBeUndefined();
    });
  
    it('Should not focus out on common elements', function() {
      var tags = ['div', 'span', 'i', 'body', 'section', 'article', 'aside', 'li', 'p', 'header', 'button', 'ion-content'];
      function setBlurred() { this.hasBlurred = true; }
      for(var x=0; x<tags.length; x++) {
        var ele = {
          tagName: tags[x],
          blur: setBlurred
        };
        tapActiveElement(ele);
        tapFocusOutActive(ele);
        expect( ele.hasBlurred ).toBeUndefined();
      }
    });
  
    it('Should focus out on input elements', function() {
      var tags = ['input', 'textarea', 'select'];
      function setBlurred() { this.hasBlurred=true; }
      for(var x=0; x<tags.length; x++) {
        var ele = {
          tagName: tags[x],
          blur: setBlurred
        };
        tapActiveElement(ele);
        tapFocusOutActive(ele);
        expect( ele.hasBlurred ).toEqual(true);
      }
    });
  
    it('Should get containing element of label when passed a label', function() {
      var label = document.createElement('label');
      expect( tapContainingElement(label).tagName ).toEqual('LABEL');
    });
  
    it('Should get containing element of div when passed a div and no parent label', function() {
      var ele = document.createElement('div');
      expect( tapContainingElement(ele).tagName ).toEqual('DIV');
    });
  
    it('Should get containing element of label when passed a labels child span', function() {
      var label = document.createElement('label');
      var span = document.createElement('span');
      label.appendChild(span);
      expect( tapContainingElement(span).tagName ).toEqual('LABEL');
    });
  
    it('Should not return itself if no parent containing label and arg allowSelf=false', function() {
      var div = document.createElement('div');
      var span = document.createElement('span');
      div.appendChild(span);
      expect( tapContainingElement(span, false) ).toBeUndefined();
    });
  
    it('Should not focus out on label element which has no child input', function() {
      var label = document.createElement('label');
      label.blur = function(){ this.hasBlurred=true; };
      tapActiveElement(label);
      tapFocusOutActive(label);
      expect( label.hasBlurred ).toBeUndefined();
    });
  
    it('Should not prevent scrolling', function() {
      var target = document.createElement('div');
      var e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(false);
    });
  
    it('Should prevent scrolling because the event has defaultedPrevented', function() {
      var target = document.createElement('div');
      var e = {
        target: target,
        defaultPrevented: true
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
    });
  
    it('Should prevent scrolling because the target is input[file]', function() {
      var target = document.createElement('input');
      target.type = 'file';
      var e = {
        target: target,
        defaultPrevented: true
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
    });
  
    it('Should prevent scrolling because the target is input[range]', function() {
      var target = document.createElement('input');
      target.type = 'range';
      var e = {
        target: target,
        defaultPrevented: true
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
    });
  
    it('Should not prevent scrolling if the target was an input or textarea', function() {
      var target = document.createElement('input');
      var e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(false);
  
      e.target = document.createElement('textarea');
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(false);
  
      e.target = document.createElement('select');
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(false);
    });
  
    it('Should not prevent scrolling if the target was an input or textarea, and the target is the same as the active element', function() {
      var target = document.createElement('input');
      tapActiveElement(target);
      var e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(false);
    });
  
    it('Should not prevent scrolling if the target isContentEditable', function() {
      var target = document.createElement('div');
      target.isContentEditable = true;
      var e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(false);
    });
  
    it('Should prevent scrolling if the target has dataset.preventScroll', function() {
      var target = document.createElement('div');
      target.setAttribute('data-prevent-scroll', 'true');
      var e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
  
      target = document.createElement('div');
      target.dataset.preventScroll = true;
      e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
  
      target = document.createElement('div');
      target.dataset.preventScroll = 'true';
      e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
    });
  
    it('Should prevent scrolling if the browser doesnt support dataset but target has data-prevent-default attribute', function() {
      var target = {
        tagName: 'div',
        getAttribute: function(val) {
          if(val === 'data-prevent-default') {
            return 'true';
          }
        }
      };
      var e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
    });
  
    it('Should prevent scrolling if the target is an object or embed', function() {
      var target = document.createElement('object');
      var e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
  
      target = document.createElement('embed');
      e = {
        target: target
      };
      expect( ionic.tap.ignoreScrollStart(e) ).toEqual(true);
    });
  
    it('Should get target element from an element', function() {
      var ele = document.createElement('div');
      expect( tapTargetElement(ele).tagName ).toEqual('DIV');
    });
  
    it('Should get labels input control from label element', function() {
      var label = document.createElement('label');
      var input = document.createElement('input');
      label.appendChild(input);
  
      expect( tapTargetElement(label).tagName ).toEqual('INPUT');
    });
  
    it('Should get button target even if it has children from es target', function() {
      var button = document.createElement('button');
      var span = document.createElement('span');
      button.appendChild(span);
  
      expect( tapTargetElement(button).tagName ).toEqual('BUTTON');
    });
  
    it('Should isTextInput', function() {
      expect( ionic.tap.isTextInput(null) ).toEqual(false);
  
      var ele = document.createElement('textarea');
      expect( ionic.tap.isTextInput(ele) ).toEqual(true);
  
      ele = document.createElement('input');
      expect( ionic.tap.isTextInput(ele) ).toEqual(true);
  
      ele.type = 'search';
      expect( ionic.tap.isTextInput(ele) ).toEqual(true);
  
      ele.type = 'url';
      expect( ionic.tap.isTextInput(ele) ).toEqual(true);
  
      ele.type = 'email';
      expect( ionic.tap.isTextInput(ele) ).toEqual(true);
  
      ele.type = 'range';
      expect( ionic.tap.isTextInput(ele) ).toEqual(false);
  
      ele.type = 'file';
      expect( ionic.tap.isTextInput(ele) ).toEqual(false);
  
      ele.type = 'submit';
      expect( ionic.tap.isTextInput(ele) ).toEqual(false);
  
      ele.type = 'reset';
      expect( ionic.tap.isTextInput(ele) ).toEqual(false);
  
      ele.type = 'radio';
      expect( ionic.tap.isTextInput(ele) ).toEqual(false);
  
      ele.type = 'checkbox';
      expect( ionic.tap.isTextInput(ele) ).toEqual(false);
  
      ele = document.createElement('select');
      expect( ionic.tap.isTextInput(ele) ).toEqual(false);
  
      ele = document.createElement('div');
      ele.setAttribute('contenteditable', true);
      expect( ionic.tap.isTextInput(ele) ).toEqual(true);
  
      ele = document.createElement('div');
      ele.setAttribute('contenteditable', false);
      expect( ionic.tap.isTextInput(ele) ).toEqual(false);
    });
  
    it('Should isLabelWithTextInput', function() {
      var label = document.createElement('label');
      expect( ionic.tap.isLabelWithTextInput(label) ).toEqual(false);
  
      var span = document.createElement('span');
      expect( ionic.tap.isLabelWithTextInput(span) ).toEqual(false);
  
      label.appendChild(span);
      expect( ionic.tap.isLabelWithTextInput(span) ).toEqual(false);
  
      var textarea = document.createElement('textarea');
      expect( ionic.tap.isLabelWithTextInput(textarea) ).toEqual(false);
  
      label.appendChild(textarea);
      expect( ionic.tap.isLabelWithTextInput(textarea) ).toEqual(true);
    });
  
    it('Should containsOrIsTextInput', function() {
      var label = document.createElement('label');
      expect( ionic.tap.containsOrIsTextInput(label) ).toEqual(false);
  
      var span = document.createElement('span');
      expect( ionic.tap.containsOrIsTextInput(span) ).toEqual(false);
  
      label.appendChild(span);
      expect( ionic.tap.containsOrIsTextInput(span) ).toEqual(false);
  
      var textarea = document.createElement('textarea');
      expect( ionic.tap.containsOrIsTextInput(textarea) ).toEqual(true);
  
      label.appendChild(textarea);
      expect( ionic.tap.containsOrIsTextInput(textarea) ).toEqual(true);
    });
  
    it('Should reset focus to tapTouchFocusedInput if the active element changed from mousedown', function() {
      tapEnabledTouchEvents = true;
      tapActiveElement(document.createElement('textarea'));
  
      var tapFocusedEle = document.createElement('input');
      tapFocusedEle.focus = function(){
        this.hasFocus = true;
      };
  
      tapTouchFocusedInput = tapFocusedEle;
  
      var e = {
        target: document.createElement('input')
      };
      tapFocusIn(e);
  
      expect( tapFocusedEle.hasFocus ).toEqual(true);
      expect( tapTouchFocusedInput ).toEqual(null);
    });
  
    it('Should tapIgnoreEvent false', function(){
      var e = {};
      expect( tapIgnoreEvent(e) ).toBeUndefined();
    });
  
    it('Should tapIgnoreEvent true because e.isTapHandled', function(){
      var e = {
        isTapHandled: true
      };
      expect( tapIgnoreEvent(e) ).toEqual(true);
    });
  
    it('Should tapIgnoreEvent true because ionic.scroll.isScrolling and target is a text input', function(){
      var target = document.createElement('textarea');
      var e = {
        target: target,
        preventDefault: function(){ this.preventedDefault=true; }
      };
      ionic.scroll.isScrolling = true;
      expect( tapIgnoreEvent(e) ).toEqual(true);
      expect( e.preventedDefault ).toEqual(true);
    });
  
    it('Should tapIgnoreEvent true because ionic.scroll.isScrolling and target is span in a label containing a text input', function(){
      var label = document.createElement('label');
      var span = document.createElement('span');
      var textarea = document.createElement('textarea');
  
      label.appendChild(span);
      label.appendChild(textarea);
  
      var e = {
        target: label,
        preventDefault: function(){ this.preventedDefault=true; }
      };
      ionic.scroll.isScrolling = true;
      expect( tapIgnoreEvent(e) ).toEqual(true);
      expect( e.preventedDefault ).toEqual(true);
    });
  
    it('Should tapIgnoreEvent false because ionic.scroll.isScrolling but target is not a text input', function(){
      var target = document.createElement('span');
      var e = {
        target: target,
        preventDefault: function(){ this.preventedDefault=true; }
      };
      ionic.scroll.isScrolling = true;
      expect( tapIgnoreEvent(e) ).toBeUndefined();
    });
  
    it('Should tapIgnoreEvent false because ionic.scroll.isScrolling but target is a input[checkbox]', function(){
      var label = document.createElement('label');
      var span = document.createElement('span');
      var checkbox = document.createElement('input');
      checkbox.type = 'checkbox';
  
      label.appendChild(span);
      label.appendChild(checkbox);
  
      var e = {
        target: span,
        preventDefault: function(){ this.preventedDefault=true; }
      };
      ionic.scroll.isScrolling = true;
      expect( tapIgnoreEvent(e) ).toBeUndefined();
    });
  
  });
  