text: /**
   * Outlayer Item
   */
  
  ( function( window ) {
  
  'use strict';
  
  // ----- get style ----- //
  
  var getComputedStyle = window.getComputedStyle;
  var getStyle = getComputedStyle ?
    function( elem ) {
      return getComputedStyle( elem, null );
    } :
    function( elem ) {
      return elem.currentStyle;
    };
  
  
  // extend objects
  function extend( a, b ) {
    for ( var prop in b ) {
      a[ prop ] = b[ prop ];
    }
    return a;
  }
  
  function isEmptyObj( obj ) {
    for ( var prop in obj ) {
      return false;
    }
    prop = null;
    return true;
  }
  
  // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
  function toDash( str ) {
    return str.replace( /([A-Z])/g, function( $1 ){
      return '-' + $1.toLowerCase();
    });
  }
  
  // -------------------------- Outlayer definition -------------------------- //
  
  function outlayerItemDefinition( EventEmitter, getSize, getStyleProperty ) {
  
  // -------------------------- CSS3 support -------------------------- //
  
  var transitionProperty = getStyleProperty('transition');
  var transformProperty = getStyleProperty('transform');
  var supportsCSS3 = transitionProperty && transformProperty;
  var is3d = !!getStyleProperty('perspective');
  
  var transitionEndEvent = {
    WebkitTransition: 'webkitTransitionEnd',
    MozTransition: 'transitionend',
    OTransition: 'otransitionend',
    transition: 'transitionend'
  }[ transitionProperty ];
  
  // properties that could have vendor prefix
  var prefixableProperties = [
    'transform',
    'transition',
    'transitionDuration',
    'transitionProperty'
  ];
  
  // cache all vendor properties
  var vendorProperties = ( function() {
    var cache = {};
    for ( var i=0, len = prefixableProperties.length; i < len; i++ ) {
      var prop = prefixableProperties[i];
      var supportedProp = getStyleProperty( prop );
      if ( supportedProp && supportedProp !== prop ) {
        cache[ prop ] = supportedProp;
      }
    }
    return cache;
  })();
  
  // -------------------------- Item -------------------------- //
  
  function Item( element, layout ) {
    if ( !element ) {
      return;
    }
  
    this.element = element;
    // parent layout class, i.e. Masonry, Isotope, or Packery
    this.layout = layout;
    this.position = {
      x: 0,
      y: 0
    };
  
    this._create();
  }
  
  // inherit EventEmitter
  extend( Item.prototype, EventEmitter.prototype );
  
  Item.prototype._create = function() {
    // transition objects
    this._transn = {
      ingProperties: {},
      clean: {},
      onEnd: {}
    };
  
    this.css({
      position: 'absolute'
    });
  };
  
  // trigger specified handler for event type
  Item.prototype.handleEvent = function( event ) {
    var method = 'on' + event.type;
    if ( this[ method ] ) {
      this[ method ]( event );
    }
  };
  
  Item.prototype.getSize = function() {
    this.size = getSize( this.element );
  };
  
  /**
   * apply CSS styles to element
   * @param {Object} style
   */
  Item.prototype.css = function( style ) {
    var elemStyle = this.element.style;
  
    for ( var prop in style ) {
      // use vendor property if available
      var supportedProp = vendorProperties[ prop ] || prop;
      elemStyle[ supportedProp ] = style[ prop ];
    }
  };
  
   // measure position, and sets it
  Item.prototype.getPosition = function() {
    var style = getStyle( this.element );
    var layoutOptions = this.layout.options;
    var isOriginLeft = layoutOptions.isOriginLeft;
    var isOriginTop = layoutOptions.isOriginTop;
    var x = parseInt( style[ isOriginLeft ? 'left' : 'right' ], 10 );
    var y = parseInt( style[ isOriginTop ? 'top' : 'bottom' ], 10 );
  
    // clean up 'auto' or other non-integer values
    x = isNaN( x ) ? 0 : x;
    y = isNaN( y ) ? 0 : y;
    // remove padding from measurement
    var layoutSize = this.layout.size;
    x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
    y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
  
    this.position.x = x;
    this.position.y = y;
  };
  
  // set settled position, apply padding
  Item.prototype.layoutPosition = function() {
    var layoutSize = this.layout.size;
    var layoutOptions = this.layout.options;
    var style = {};
  
    if ( layoutOptions.isOriginLeft ) {
      style.left = ( this.position.x + layoutSize.paddingLeft ) + 'px';
      // reset other property
      style.right = '';
    } else {
      style.right = ( this.position.x + layoutSize.paddingRight ) + 'px';
      style.left = '';
    }
  
    if ( layoutOptions.isOriginTop ) {
      style.top = ( this.position.y + layoutSize.paddingTop ) + 'px';
      style.bottom = '';
    } else {
      style.bottom = ( this.position.y + layoutSize.paddingBottom ) + 'px';
      style.top = '';
    }
  
    this.css( style );
    this.emitEvent( 'layout', [ this ] );
  };
  
  
  // transform translate function
  var translate = is3d ?
    function( x, y ) {
      return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
    } :
    function( x, y ) {
      return 'translate(' + x + 'px, ' + y + 'px)';
    };
  
  
  Item.prototype._transitionTo = function( x, y ) {
    this.getPosition();
    // get current x & y from top/left
    var curX = this.position.x;
    var curY = this.position.y;
  
    var compareX = parseInt( x, 10 );
    var compareY = parseInt( y, 10 );
    var didNotMove = compareX === this.position.x && compareY === this.position.y;
  
    // save end position
    this.setPosition( x, y );
  
    // if did not move and not transitioning, just go to layout
    if ( didNotMove && !this.isTransitioning ) {
      this.layoutPosition();
      return;
    }
  
    var transX = x - curX;
    var transY = y - curY;
    var transitionStyle = {};
    // flip cooridinates if origin on right or bottom
    var layoutOptions = this.layout.options;
    transX = layoutOptions.isOriginLeft ? transX : -transX;
    transY = layoutOptions.isOriginTop ? transY : -transY;
    transitionStyle.transform = translate( transX, transY );
  
    this.transition({
      to: transitionStyle,
      onTransitionEnd: {
        transform: this.layoutPosition
      },
      isCleaning: true
    });
  };
  
  // non transition + transform support
  Item.prototype.goTo = function( x, y ) {
    this.setPosition( x, y );
    this.layoutPosition();
  };
  
  // use transition and transforms if supported
  Item.prototype.moveTo = supportsCSS3 ?
    Item.prototype._transitionTo : Item.prototype.goTo;
  
  Item.prototype.setPosition = function( x, y ) {
    this.position.x = parseInt( x, 10 );
    this.position.y = parseInt( y, 10 );
  };
  
  // ----- transition ----- //
  
  /**
   * @param {Object} style - CSS
   * @param {Function} onTransitionEnd
   */
  
  // non transition, just trigger callback
  Item.prototype._nonTransition = function( args ) {
    this.css( args.to );
    if ( args.isCleaning ) {
      this._removeStyles( args.to );
    }
    for ( var prop in args.onTransitionEnd ) {
      args.onTransitionEnd[ prop ].call( this );
    }
  };
  
  /**
   * proper transition
   * @param {Object} args - arguments
   *   @param {Object} to - style to transition to
   *   @param {Object} from - style to start transition from
   *   @param {Boolean} isCleaning - removes transition styles after transition
   *   @param {Function} onTransitionEnd - callback
   */
  Item.prototype._transition = function( args ) {
    // redirect to nonTransition if no transition duration
    if ( !parseFloat( this.layout.options.transitionDuration ) ) {
      this._nonTransition( args );
      return;
    }
  
    var _transition = this._transn;
    // keep track of onTransitionEnd callback by css property
    for ( var prop in args.onTransitionEnd ) {
      _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
    }
    // keep track of properties that are transitioning
    for ( prop in args.to ) {
      _transition.ingProperties[ prop ] = true;
      // keep track of properties to clean up when transition is done
      if ( args.isCleaning ) {
        _transition.clean[ prop ] = true;
      }
    }
  
    // set from styles
    if ( args.from ) {
      this.css( args.from );
      // force redraw. http://blog.alexmaccaw.com/css-transitions
      var h = this.element.offsetHeight;
      // hack for JSHint to hush about unused var
      h = null;
    }
    // enable transition
    this.enableTransition( args.to );
    // set styles that are transitioning
    this.css( args.to );
  
    this.isTransitioning = true;
  
  };
  
  var itemTransitionProperties = transformProperty && ( toDash( transformProperty ) +
    ',opacity' );
  
  Item.prototype.enableTransition = function(/* style */) {
    // only enable if not already transitioning
    // bug in IE10 were re-setting transition style will prevent
    // transitionend event from triggering
    if ( this.isTransitioning ) {
      return;
    }
  
    // make transition: foo, bar, baz from style object
    // TODO uncomment this bit when IE10 bug is resolved
    // var transitionValue = [];
    // for ( var prop in style ) {
    //   // dash-ify camelCased properties like WebkitTransition
    //   transitionValue.push( toDash( prop ) );
    // }
    // enable transition styles
    // HACK always enable transform,opacity for IE10
    this.css({
      transitionProperty: itemTransitionProperties,
      transitionDuration: this.layout.options.transitionDuration
    });
    // listen for transition end event
    this.element.addEventListener( transitionEndEvent, this, false );
  };
  
  Item.prototype.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ];
  
  // ----- events ----- //
  
  Item.prototype.onwebkitTransitionEnd = function( event ) {
    this.ontransitionend( event );
  };
  
  Item.prototype.onotransitionend = function( event ) {
    this.ontransitionend( event );
  };
  
  // properties that I munge to make my life easier
  var dashedVendorProperties = {
    '-webkit-transform': 'transform',
    '-moz-transform': 'transform',
    '-o-transform': 'transform'
  };
  
  Item.prototype.ontransitionend = function( event ) {
    // disregard bubbled events from children
    if ( event.target !== this.element ) {
      return;
    }
    var _transition = this._transn;
    // get property name of transitioned property, convert to prefix-free
    var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
  
    // remove property that has completed transitioning
    delete _transition.ingProperties[ propertyName ];
    // check if any properties are still transitioning
    if ( isEmptyObj( _transition.ingProperties ) ) {
      // all properties have completed transitioning
      this.disableTransition();
    }
    // clean style
    if ( propertyName in _transition.clean ) {
      // clean up style
      this.element.style[ event.propertyName ] = '';
      delete _transition.clean[ propertyName ];
    }
    // trigger onTransitionEnd callback
    if ( propertyName in _transition.onEnd ) {
      var onTransitionEnd = _transition.onEnd[ propertyName ];
      onTransitionEnd.call( this );
      delete _transition.onEnd[ propertyName ];
    }
  
    this.emitEvent( 'transitionEnd', [ this ] );
  };
  
  Item.prototype.disableTransition = function() {
    this.removeTransitionStyles();
    this.element.removeEventListener( transitionEndEvent, this, false );
    this.isTransitioning = false;
  };
  
  /**
   * removes style property from element
   * @param {Object} style
  **/
  Item.prototype._removeStyles = function( style ) {
    // clean up transition styles
    var cleanStyle = {};
    for ( var prop in style ) {
      cleanStyle[ prop ] = '';
    }
    this.css( cleanStyle );
  };
  
  var cleanTransitionStyle = {
    transitionProperty: '',
    transitionDuration: ''
  };
  
  Item.prototype.removeTransitionStyles = function() {
    // remove transition
    this.css( cleanTransitionStyle );
  };
  
  // ----- show/hide/remove ----- //
  
  // remove element from DOM
  Item.prototype.removeElem = function() {
    this.element.parentNode.removeChild( this.element );
    this.emitEvent( 'remove', [ this ] );
  };
  
  Item.prototype.remove = function() {
    // just remove element if no transition support or no transition
    if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
      this.removeElem();
      return;
    }
  
    // start transition
    var _this = this;
    this.on( 'transitionEnd', function() {
      _this.removeElem();
      return true; // bind once
    });
    this.hide();
  };
  
  Item.prototype.reveal = function() {
    delete this.isHidden;
    // remove display: none
    this.css({ display: '' });
  
    var options = this.layout.options;
    this.transition({
      from: options.hiddenStyle,
      to: options.visibleStyle,
      isCleaning: true
    });
  };
  
  Item.prototype.hide = function() {
    // set flag
    this.isHidden = true;
    // remove display: none
    this.css({ display: '' });
  
    var options = this.layout.options;
    this.transition({
      from: options.visibleStyle,
      to: options.hiddenStyle,
      // keep hidden stuff hidden
      isCleaning: true,
      onTransitionEnd: {
        opacity: function() {
          // check if still hidden
          // during transition, item may have been un-hidden
          if ( this.isHidden ) {
            this.css({ display: 'none' });
          }
        }
      }
    });
  };
  
  Item.prototype.destroy = function() {
    this.css({
      position: '',
      left: '',
      right: '',
      top: '',
      bottom: '',
      transition: '',
      transform: ''
    });
  };
  
  return Item;
  
  }
  
  // -------------------------- transport -------------------------- //
  
  if ( typeof define === 'function' && define.amd ) {
    // AMD
    define( [
        'eventEmitter/EventEmitter',
        'get-size/get-size',
        'get-style-property/get-style-property'
      ],
      outlayerItemDefinition );
  } else if (typeof exports === 'object') {
    // CommonJS
    module.exports = outlayerItemDefinition(
      require('wolfy87-eventemitter'),
      require('get-size'),
      require('desandro-get-style-property')
    );
  } else {
    // browser global
    window.Outlayer = {};
    window.Outlayer.Item = outlayerItemDefinition(
      window.EventEmitter,
      window.getSize,
      window.getStyleProperty
    );
  }
  
  })( window );
  