<commit_before>/**
 *  class Element.Layout
**/
Element.Layout = Class.create({
  /**
   *  new Element.Layout(element[, options])
   *  Returns a versatile measurement object that quacks several ways. Can be
   *  coerced into a hash, an object, or JSON.
  **/
  initialize: function(element, options) {
    this.element = $(element);
    this.options = Object.extend({
      dimensions: true,
      offsets:    false
    }, options || {});

    this.layout = new Hash();

    this.getDimensions();
  },

  getDimensions: function() {
    var element = this.element,
        display = element.getStyle('display'),
        noOffsetWidth;

    // The style object is inaccessible in Safari <= 2.0 when the element
    // is hidden.
    var isNotShown = display === "none" || display === null;
    var isTable = element.tagName.toUpperCase() == 'TABLE';

    // If the element is hidden, we show it for an instant
    // to grab its dimensions.
    if (isNotShown) {
      var style = element.style;
      var originalStyle = {
        visibility: style.visibility,
        position:   style.position,
        display:    style.display
      };

      Object.extend(style, {
        visibility: 'hidden',
        position:   'absolute',
        display:    'block'
      });
    }

    if (this.options.dimensions === true) {
      // clientWidth includes margin offsets of a table in Mozilla,
      // set offsets to 0, get width value, then revert back
      if (isTable) {
        var originalLeft = element.style.marginLeft;
        var originalRight = element.style.marginRight;
        element.style.marginLeft = '0px';
        element.style.marginRight = '0px';
        noOffsetWidth = element.clientWidth;
        element.style.marginLeft = originalLeft;
        element.style.marginRight = originalRight;
      }

      var paddingBox = {
        width:  noOffsetWidth || element.clientWidth,
        height: element.clientHeight
      };

      // For backwards-compatibility, the returned object will have
      // width and height equal to the padding-box values.
      this.layout.update(paddingBox);

      this.layout.set('paddingBox', paddingBox);

      var padding = this.getStyleValuesFor('padding', 'trbl');
      this.layout.set('padding', padding);

      var contentBox = {
        width:  paddingBox.width  - padding.left - padding.right,
        height: paddingBox.height - padding.top  - padding.bottom
      };

      this.layout.set('contentBox', contentBox);

      var border = this.getStyleValuesFor('border', 'trbl');
      this.layout.set('border', border);

      var borderBox = {
        width:  paddingBox.width  + border.left + border.right,
        height: paddingBox.height + border.top  + border.bottom
      };

      this.layout.set('borderBox', borderBox);

    } // dimensions

    if (this.options.offsets === true) {
      var offsets = {};

      offsets.positioned = this.offset();
      offsets.viewport   = this.viewportOffset();
      offsets.scroll     = this.scrollOffset();
      offsets.document   = this.documentOffset();

      this.layout.update('offsets', offsets);
    } // offsets

    // If we altered the element's styles, return them to their
    // original values.
    if (isNotShown) {
      Object.extend(style, originalStyle);
    }

    return this.layout;
  },

  // Converts a raw CSS value like '9px' or '1em' to
  // a number (in pixels).
  // IE: Redefined below
  cssToNumber: function(property) {
    return window.parseFloat(this.element.getStyle(property));
  },

  // sidesNeeded argument is a string.
  // "trbl" = top, right, bottom, left
  // "tb"   = top, bottom
  getStyleValuesFor: function(property, sidesNeeded) {
    var sides = $w('top bottom left right');
    var propertyNames = sides.map( function(s) {
      return property + s.capitalize();
    });

    if (property === 'border') {
      propertyNames = propertyNames.map( function(p) {
        return p + 'Width';
      });
    }

    var values = {};

    sides.each( function(side, index) {
      if (!sidesNeeded.include(side.charAt(0))) return;
      values[side] = this.cssToNumber(propertyNames[index]);
    }, this);

    return values;
  },

  toObject: function() {
    return this.layout.toObject();
  },

  toHash: function() {
    return this.layout;
  },

  toJSON: function() {
    return this.layout.toJSON();
  },

  toCSS: function() {
    var css = {}, d = this.layout;
    if (this.options.dimensions) {
      var margins = $w('top right bottom left').map( function(side) {
        return d.margin[side] + 'px';
      }).join(' ');
      var padding = $w('top right bottom left').map( function(side) {
        return d.padding[side] + 'px';
      }).join(' ');

      Object.extend(css, {
        width:   d.contentBox.width  + 'px',
        height:  d.contentBox.height + 'px',
        margin:  margins,
        padding: padding
      });
    }

    if (this.options.offsets) {
      Object.extend(css, {
        left: d.offset.left + 'px',
        top:  d.offset.top  + 'px'
      });
    }

    return css;
  },

  /**
   *  Element.Layout#dimensions() -> Element.Coordinates
   *  Reports the dimensions of the given element.
  **/
  dimensions: function() {
    var box = this.layout.get('contentBox');
    return { width: box.width, height: box.height };
  },

  /**
   *  Element.Layout#offset([element=document]) -> Element.Coordinates
   *  Positioned offset. Measured from offset parent.
  **/
  offset: function() {
    return this.layout.offsets.positioned;
  },

  /**
   *  Element.Layout#viewportOffset() -> Element.Coordinates
   *  Reports the element's top- and left-distance from the upper-left
   *  corner of the viewport.
  **/
  viewportOffset: function() {
    var element = this.element;

    // IE and FF >= 3 provide getBoundingClientRect, a much quicker path
    // to retrieving viewport offset.
    if (element.getBoundingClientRect) {
      var d = element.getBoundingClientRect();
      return { left: Math.round(d.left), top: Math.round(d.top) };
    }

    var valueT = 0, valueL = 0, element = this.element;

    // First collect cumulative offsets
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent == document.body &&
        Element.getStyle(element, 'position') === 'absolute') break;

    } while (element = element.offsetParent);

    // Then subtract cumulative scroll offsets
    element = this.element;
    do {
      if (!Prototype.Browser.Opera || element.tagName.toUpperCase() == 'BODY') {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return { left: valueL, top: valueT };
  },

  /**
   *  Element.Layout#documentOffset() -> Element.Coords
   *  Reports the element's top- and left-distance from the upper-left
   *  corner of its containing document.
  **/
  documentOffset: function() {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return Element.Layout.normalize({ left: valueL, top: valueT });
  },

  /**
   *  Element.Layout#offsetParent() -> Element
   *  Returns the element's positioning context — the nearest ancestor
   *  with a CSS "position" value other than "static."
  **/
  offsetParent: function(element) {
    if (element.offsetParent) return $(element.offsetParent);
    if (element == document.body) return $(element);

    while ((element = element.parentNode) && element !== document.body)
      if (Element.getStyle(element, 'position') !== 'static')
        return element;

    return $(document.body);
  },

  /**
   *  Element.Layout#scrollOffset(@element) -> Object
   *  Reports the element's top- and left-distance from the upper-left
   *  corner of its containing document, compensating for the scroll
   *  offsets of any ancestors.
  **/
  scrollOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return Element.Layout.normalize({ left: valueL, top: valueT });
  }

});

if (Prototype.Browser.IE) {
  Element.Layout.addMethods({
    // IE gives the literal cascaded style, not the computed style.
    // We need to ensure pixel values are returned.
    cssToNumber: function(property) {
      var value = this.element.getStyle(property);

      if ((/^\d+(px)?$/i).test(value))
        return window.parseFloat(value);

      // If the unit is something other than a pixel (em, pt, %), set it on
      // something we can grab a pixel value from.
      var element = this.element;

      var sl = element.style.left, rsl = element.runtimeStyle.left;

      element.runtimeStyle.left = element.currentStyle.left;
      element.style.left = value || 0;

      value = element.style.pixelLeft;

      element.style.left = sl;
      element.runtimeStyle.left = rsl;

      return value;
    }
  });
}

// Acts like an array for backwards-compatibility.
Element.Layout.normalize = function(obj) {
  var arr = [];
  arr[0] = ('left' in obj) ? obj.left : obj.width;
  arr[1] = ('top'  in obj) ? obj.top  : obj.height;
  return Object.extend(arr, obj);
};

Object.extend(Element.Methods, {
  /**
   *  Element#getLayout(@element[, options]) -> Object
   *  Reports the dimensions and offsets of the given element.
   *
   *  By default, `getLayout` will return as much information about the
   *  element as possible: dimensions for the content, padding, and border
   *  boxes; and viewport, cumulative, scroll, and positioned offsets.
   *  The `options` argument can be used to bypass checks you don't need
   *  when speed is of the utmost importance.
  **/
  getLayout: function(element, options) {
    return new Element.Layout(element, options).toObject();
  },

  getDimensions: function(element) {
    return new Element.Layout(element, { offsets: false }).toObject();
  },

  getHeight: function(element) {
    return Element.getDimensions(element).height;
  },

  getWidth: function(element) {
    return Element.getDimensions(element).width;
  },

  getOffsets: function(element) {
    return new Element.Layout(element, { dimensions: false}).toObject();
  },

  viewportOffset: function(element) {
    return Element.getOffsets(element).viewport;
  },

  cumulativeOffset: function(element) {
    return Element.getOffsets(element).document;
  },

  /**
   *  Element#cumulativeScrollOffset(@element) -> Object
   *  Reports the element's top- and left-distance from the upper-left
   *  corner of its containing document, compensating for the scroll
   *  offsets of any ancestors.
  **/
  cumulativeScrollOffset: function(element) {
    return Element.getOffsets(element).scroll;
  },

  /**
   *  Element#cumulativeOffset(@element) -> Object
   *  Reports the element's top- and left-distance from its positioning
   *  parent.
  **/
  positionedOffset: function(element) {
    return Element.getOffsets(element).positioned;
  },

  /**
   *  Element#absolutize(@element) -> Element
   *  Switches element from static/relative positioning to absolute
   *  positioning while maintaining the element's size and position.
  **/
  absolutize: function(element) {
    element = $(element);
    if (element.getStyle('position') === 'absolute') return element;

    var offsets = element.positionedOffset();
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    Object.extend(element, {
      _originalLeft:   left - parseFloat(element.style.left || 0),
      _originalTop:    top  - parseFloat(element.style.top  || 0),
      _originalWidth:  element.style.width,
      _originalHeight: element.style.height
    });

    element.setStyle({
      position: 'absolute',
      top:      top + 'px',
      left:     left + 'px',
      width:    width + 'px',
      height:   height + 'px'
    });

    return element;
  },

  /**
   *  Element#relativize(@element) -> Element
   *  Reverts element from absolute positioning to relative positioning
   *  while maintaining the element's size and position.
  **/
  relativize: function(element) {
    element = $(element);
    if (element.getStyle('position') === 'relative') return element;

    if (Object.isUndefined(element._originalTop)) {
      throw "Element#absolutize must be called first.";
    }

    element.setStyle({ position: 'relative' });

    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.setStyle({
      top:      top + 'px',
      left:     left + 'px',
      width:    element._originalHeight + 'px',
      height:   element._originalWidth  + 'px'
    });

    return element;
  },

  /**
   *  Element#getOffsetParent(@element) -> Element
   *  Returns the element's positioning context — the nearest ancestor
   *  with a CSS "position" value other than "static."
  **/
  getOffsetParent: function(element) {
    if (element.offsetParent) return $(element.offsetParent);
    if (element == document.body) return $(element);

    while ((element = element.parentNode) && element !== document.body)
      if (Element.getStyle(element, 'position') !== 'static')
        return element;

    return $(document.body);
  },

  clonePosition: function(element, source) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || { });

    // find page position of source
    source = $(source);
    var sourceLayout = new Element.Layout(source).toCSS();

    if (!options.setHeight) delete sourceLayout.height;
    if (!options.setWidth)  delete sourceLayout.width;
    if (!options.setLeft)   delete sourceLayout.left;
    if (!options.setTop)    delete sourceLayout.top;

    $(element).setStyle(souceLayout);

    return element;
  }
});

document.viewport = {
  /**
   *  document.viewport.getDimensions () -> Object
   *  Returns the height and width of the browser viewport.
  **/
  getDimensions: function() {
    var dimensions = { };
    var B = Prototype.Browser;
    $w('width height').each(function(d) {
      var D = d.capitalize();
      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
    });
    return dimensions;
  },

  /**
   *  document.viewport.getWidth() -> Number
   *  Returns the width of the browser viewport.
  **/
  getWidth: function() {
    return this.getDimensions().width;
  },

  /**
   *  document.viewport.getHeight() -> Number
   *  Returns the height of the browser viewport.
  **/
  getHeight: function() {
    return this.getDimensions().height;
  },

  /**
   *  document.viewport.getScrollOffsets() -> Object
   *  Returns the distances the viewport has been scrolled in the
   *  horizontal and vertical directions.
  **/
  getScrollOffsets: function() {
    return Element.Dimensions.normalize({
      left: window.pageXOffset
        || document.documentElement.scrollLeft
        || document.body.scrollLeft,
      top: window.pageYOffset
        || document.documentElement.scrollTop
        || document.body.scrollTop
    });
  }
};
<commit_msg>missing documentation<commit_after>/**
 *  class Element.Layout
**/
Element.Layout = Class.create({
  /**
   *  new Element.Layout(element[, options])
   *  Returns a versatile measurement object that quacks several ways. Can be
   *  coerced into a hash, an object, or JSON.
  **/
  initialize: function(element, options) {
    this.element = $(element);
    this.options = Object.extend({
      dimensions: true,
      offsets:    false
    }, options || {});

    this.layout = new Hash();

    this.getDimensions();
  },

  getDimensions: function() {
    var element = this.element,
        display = element.getStyle('display'),
        noOffsetWidth;

    // The style object is inaccessible in Safari <= 2.0 when the element
    // is hidden.
    var isNotShown = display === "none" || display === null;
    var isTable = element.tagName.toUpperCase() == 'TABLE';

    // If the element is hidden, we show it for an instant
    // to grab its dimensions.
    if (isNotShown) {
      var style = element.style;
      var originalStyle = {
        visibility: style.visibility,
        position:   style.position,
        display:    style.display
      };

      Object.extend(style, {
        visibility: 'hidden',
        position:   'absolute',
        display:    'block'
      });
    }

    if (this.options.dimensions === true) {
      // clientWidth includes margin offsets of a table in Mozilla,
      // set offsets to 0, get width value, then revert back
      if (isTable) {
        var originalLeft = element.style.marginLeft;
        var originalRight = element.style.marginRight;
        element.style.marginLeft = '0px';
        element.style.marginRight = '0px';
        noOffsetWidth = element.clientWidth;
        element.style.marginLeft = originalLeft;
        element.style.marginRight = originalRight;
      }

      var paddingBox = {
        width:  noOffsetWidth || element.clientWidth,
        height: element.clientHeight
      };

      // For backwards-compatibility, the returned object will have
      // width and height equal to the padding-box values.
      this.layout.update(paddingBox);

      this.layout.set('paddingBox', paddingBox);

      var padding = this.getStyleValuesFor('padding', 'trbl');
      this.layout.set('padding', padding);

      var contentBox = {
        width:  paddingBox.width  - padding.left - padding.right,
        height: paddingBox.height - padding.top  - padding.bottom
      };

      this.layout.set('contentBox', contentBox);

      var border = this.getStyleValuesFor('border', 'trbl');
      this.layout.set('border', border);

      var borderBox = {
        width:  paddingBox.width  + border.left + border.right,
        height: paddingBox.height + border.top  + border.bottom
      };

      this.layout.set('borderBox', borderBox);

    } // dimensions

    if (this.options.offsets === true) {
      var offsets = {};

      offsets.positioned = this.offset();
      offsets.viewport   = this.viewportOffset();
      offsets.scroll     = this.scrollOffset();
      offsets.document   = this.documentOffset();

      this.layout.update('offsets', offsets);
    } // offsets

    // If we altered the element's styles, return them to their
    // original values.
    if (isNotShown) {
      Object.extend(style, originalStyle);
    }

    return this.layout;
  },

  // Converts a raw CSS value like '9px' or '1em' to
  // a number (in pixels).
  // IE: Redefined below
  cssToNumber: function(property) {
    return window.parseFloat(this.element.getStyle(property));
  },

  // sidesNeeded argument is a string.
  // "trbl" = top, right, bottom, left
  // "tb"   = top, bottom
  getStyleValuesFor: function(property, sidesNeeded) {
    var sides = $w('top bottom left right');
    var propertyNames = sides.map( function(s) {
      return property + s.capitalize();
    });

    if (property === 'border') {
      propertyNames = propertyNames.map( function(p) {
        return p + 'Width';
      });
    }

    var values = {};

    sides.each( function(side, index) {
      if (!sidesNeeded.include(side.charAt(0))) return;
      values[side] = this.cssToNumber(propertyNames[index]);
    }, this);

    return values;
  },

  toObject: function() {
    return this.layout.toObject();
  },

  toHash: function() {
    return this.layout;
  },

  toJSON: function() {
    return this.layout.toJSON();
  },

  toCSS: function() {
    var css = {}, d = this.layout;
    if (this.options.dimensions) {
      var margins = $w('top right bottom left').map( function(side) {
        return d.margin[side] + 'px';
      }).join(' ');
      var padding = $w('top right bottom left').map( function(side) {
        return d.padding[side] + 'px';
      }).join(' ');

      Object.extend(css, {
        width:   d.contentBox.width  + 'px',
        height:  d.contentBox.height + 'px',
        margin:  margins,
        padding: padding
      });
    }

    if (this.options.offsets) {
      Object.extend(css, {
        left: d.offset.left + 'px',
        top:  d.offset.top  + 'px'
      });
    }

    return css;
  },

  /**
   *  Element.Layout#dimensions() -> Element.Coordinates
   *  Reports the dimensions of the given element.
  **/
  dimensions: function() {
    var box = this.layout.get('contentBox');
    return { width: box.width, height: box.height };
  },

  /**
   *  Element.Layout#offset([element=document]) -> Element.Coordinates
   *  Positioned offset. Measured from offset parent.
  **/
  offset: function() {
    return this.layout.offsets.positioned;
  },

  /**
   *  Element.Layout#viewportOffset() -> Element.Coordinates
   *  Reports the element's top- and left-distance from the upper-left
   *  corner of the viewport.
  **/
  viewportOffset: function() {
    var element = this.element;

    // IE and FF >= 3 provide getBoundingClientRect, a much quicker path
    // to retrieving viewport offset.
    if (element.getBoundingClientRect) {
      var d = element.getBoundingClientRect();
      return { left: Math.round(d.left), top: Math.round(d.top) };
    }

    var valueT = 0, valueL = 0, element = this.element;

    // First collect cumulative offsets
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent == document.body &&
        Element.getStyle(element, 'position') === 'absolute') break;

    } while (element = element.offsetParent);

    // Then subtract cumulative scroll offsets
    element = this.element;
    do {
      if (!Prototype.Browser.Opera || element.tagName.toUpperCase() == 'BODY') {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return { left: valueL, top: valueT };
  },

  /**
   *  Element.Layout#documentOffset() -> Element.Coords
   *  Reports the element's top- and left-distance from the upper-left
   *  corner of its containing document.
  **/
  documentOffset: function() {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return Element.Layout.normalize({ left: valueL, top: valueT });
  },

  /**
   *  Element.Layout#offsetParent() -> Element
   *  Returns the element's positioning context — the nearest ancestor
   *  with a CSS "position" value other than "static."
  **/
  offsetParent: function(element) {
    if (element.offsetParent) return $(element.offsetParent);
    if (element == document.body) return $(element);

    while ((element = element.parentNode) && element !== document.body)
      if (Element.getStyle(element, 'position') !== 'static')
        return element;

    return $(document.body);
  },

  /**
   *  Element.Layout#scrollOffset(@element) -> Object
   *  Reports the element's top- and left-distance from the upper-left
   *  corner of its containing document, compensating for the scroll
   *  offsets of any ancestors.
  **/
  scrollOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return Element.Layout.normalize({ left: valueL, top: valueT });
  }

});

if (Prototype.Browser.IE) {
  Element.Layout.addMethods({
    // IE gives the literal cascaded style, not the computed style.
    // We need to ensure pixel values are returned.
    cssToNumber: function(property) {
      var value = this.element.getStyle(property);

      if ((/^\d+(px)?$/i).test(value))
        return window.parseFloat(value);

      // If the unit is something other than a pixel (em, pt, %), set it on
      // something we can grab a pixel value from.
      var element = this.element;

      var sl = element.style.left, rsl = element.runtimeStyle.left;

      element.runtimeStyle.left = element.currentStyle.left;
      element.style.left = value || 0;

      value = element.style.pixelLeft;

      element.style.left = sl;
      element.runtimeStyle.left = rsl;

      return value;
    }
  });
}

// Acts like an array for backwards-compatibility.
Element.Layout.normalize = function(obj) {
  var arr = [];
  arr[0] = ('left' in obj) ? obj.left : obj.width;
  arr[1] = ('top'  in obj) ? obj.top  : obj.height;
  return Object.extend(arr, obj);
};

Object.extend(Element.Methods, {
  /**
   *  Element#getLayout(@element[, options]) -> Object
   *  Reports the dimensions and offsets of the given element.
   *
   *  By default, `getLayout` will return as much information about the
   *  element as possible: dimensions for the content, padding, and border
   *  boxes; and viewport, cumulative, scroll, and positioned offsets.
   *  The `options` argument can be used to bypass checks you don't need
   *  when speed is of the utmost importance.
  **/
  getLayout: function(element, options) {
    return new Element.Layout(element, options).toObject();
  },

  getDimensions: function(element) {
    return new Element.Layout(element, { offsets: false }).toObject();
  },

  /**
   *  Element#getHeight(@element) -> Number
   *  Returns the height of the element.
  **/
  getHeight: function(element) {
    return Element.getDimensions(element).height;
  },

  /**
   *  Element#getWidth(@element) -> Number
   *  Returns the width of the element.
  **/
  getWidth: function(element) {
    return Element.getDimensions(element).width;
  },

  getOffsets: function(element) {
    return new Element.Layout(element, { dimensions: false}).toObject();
  },

  viewportOffset: function(element) {
    return Element.getOffsets(element).viewport;
  },

  cumulativeOffset: function(element) {
    return Element.getOffsets(element).document;
  },

  /**
   *  Element#cumulativeScrollOffset(@element) -> Object
   *  Reports the element's top- and left-distance from the upper-left
   *  corner of its containing document, compensating for the scroll
   *  offsets of any ancestors.
  **/
  cumulativeScrollOffset: function(element) {
    return Element.getOffsets(element).scroll;
  },

  /**
   *  Element#cumulativeOffset(@element) -> Object
   *  Reports the element's top- and left-distance from its positioning
   *  parent.
  **/
  positionedOffset: function(element) {
    return Element.getOffsets(element).positioned;
  },

  /**
   *  Element#absolutize(@element) -> Element
   *  Switches element from static/relative positioning to absolute
   *  positioning while maintaining the element's size and position.
  **/
  absolutize: function(element) {
    element = $(element);
    if (element.getStyle('position') === 'absolute') return element;

    var offsets = element.positionedOffset();
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    Object.extend(element, {
      _originalLeft:   left - parseFloat(element.style.left || 0),
      _originalTop:    top  - parseFloat(element.style.top  || 0),
      _originalWidth:  element.style.width,
      _originalHeight: element.style.height
    });

    element.setStyle({
      position: 'absolute',
      top:      top + 'px',
      left:     left + 'px',
      width:    width + 'px',
      height:   height + 'px'
    });

    return element;
  },

  /**
   *  Element#relativize(@element) -> Element
   *  Reverts element from absolute positioning to relative positioning
   *  while maintaining the element's size and position.
  **/
  relativize: function(element) {
    element = $(element);
    if (element.getStyle('position') === 'relative') return element;

    if (Object.isUndefined(element._originalTop)) {
      throw "Element#absolutize must be called first.";
    }

    element.setStyle({ position: 'relative' });

    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.setStyle({
      top:      top + 'px',
      left:     left + 'px',
      width:    element._originalHeight + 'px',
      height:   element._originalWidth  + 'px'
    });

    return element;
  },

  /**
   *  Element#getOffsetParent(@element) -> Element
   *  Returns the element's positioning context — the nearest ancestor
   *  with a CSS "position" value other than "static."
  **/
  getOffsetParent: function(element) {
    if (element.offsetParent) return $(element.offsetParent);
    if (element == document.body) return $(element);

    while ((element = element.parentNode) && element !== document.body)
      if (Element.getStyle(element, 'position') !== 'static')
        return element;

    return $(document.body);
  },

  clonePosition: function(element, source) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || { });

    // find page position of source
    source = $(source);
    var sourceLayout = new Element.Layout(source).toCSS();

    if (!options.setHeight) delete sourceLayout.height;
    if (!options.setWidth)  delete sourceLayout.width;
    if (!options.setLeft)   delete sourceLayout.left;
    if (!options.setTop)    delete sourceLayout.top;

    $(element).setStyle(souceLayout);

    return element;
  }
});

document.viewport = {
  /**
   *  document.viewport.getDimensions () -> Object
   *  Returns the height and width of the browser viewport.
  **/
  getDimensions: function() {
    var dimensions = { };
    var B = Prototype.Browser;
    $w('width height').each(function(d) {
      var D = d.capitalize();
      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
    });
    return dimensions;
  },

  /**
   *  document.viewport.getWidth() -> Number
   *  Returns the width of the browser viewport.
  **/
  getWidth: function() {
    return this.getDimensions().width;
  },

  /**
   *  document.viewport.getHeight() -> Number
   *  Returns the height of the browser viewport.
  **/
  getHeight: function() {
    return this.getDimensions().height;
  },

  /**
   *  document.viewport.getScrollOffsets() -> Object
   *  Returns the distances the viewport has been scrolled in the
   *  horizontal and vertical directions.
  **/
  getScrollOffsets: function() {
    return Element.Dimensions.normalize({
      left: window.pageXOffset
        || document.documentElement.scrollLeft
        || document.body.scrollLeft,
      top: window.pageYOffset
        || document.documentElement.scrollTop
        || document.body.scrollTop
    });
  }
};
<|endoftext|>


<commit_before>/*
 * Copyright 2013 The Polymer Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

(function(scope) {

  /**
   * This module is for normalizing events. Mouse and Touch events will be
   * collected here, and fire PointerEvents that have the same semantics, no
   * matter the source.
   * Events fired:
   *   - pointerdown: a pointing is added
   *   - pointerup: a pointer is removed
   *   - pointermove: a pointer is moved
   *   - pointerover: a pointer crosses into an element
   *   - pointerout: a pointer leaves an element
   *   - pointercancel: a pointer will no longer generate events
   */
  var dispatcher = {
    targets: new scope.SideTable,
    handledEvents: new scope.SideTable,
    scrollType: new scope.SideTable,
    pointermap: new scope.PointerMap,
    eventMap: {},
    // Scope objects for native events.
    // This exists for ease of testing.
    eventSources: {},
    eventSourceList: [],
    /**
     * Add a new event source that will generate pointer events.
     *
     * `inSource` must contain an array of event names named `events`, and
     * functions with the names specified in the `events` array.
     * @param {string} name A name for the event source
     * @param {Object} source A new source of platform events.
     */
    registerSource: function(name, source) {
      var s = source;
      var newEvents = s.events;
      if (newEvents) {
        newEvents.forEach(function(e) {
          if (s[e]) {
            this.eventMap[e] = s[e].bind(s);
          }
        }, this);
        this.eventSources[name] = s;
        this.eventSourceList.push(s);
      }
    },
    register: function(element) {
      var l = this.eventSourceList.length;
      for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
        // call eventsource register
        es.register.call(es, element);
      }
    },
    unregister: function(element) {
      var l = this.eventSourceList.length;
      for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
        // call eventsource register
        es.unregister.call(es, element);
      }
    },
    // EVENTS
    down: function(inEvent) {
      this.fireEvent('pointerdown', inEvent)
    },
    move: function(inEvent) {
      this.fireEvent('pointermove', inEvent);
    },
    up: function(inEvent) {
      this.fireEvent('pointerup', inEvent);
    },
    enter: function(inEvent) {
      inEvent.bubbles = false;
      this.fireEvent('pointerenter', inEvent)
    },
    leave: function(inEvent) {
      inEvent.bubbles = false;
      this.fireEvent('pointerleave', inEvent);
    },
    over: function(inEvent) {
      inEvent.bubbles = true;
      this.fireEvent('pointerover', inEvent)
    },
    out: function(inEvent) {
      inEvent.bubbles = true;
      this.fireEvent('pointerout', inEvent);
    },
    cancel: function(inEvent) {
      this.fireEvent('pointercancel', inEvent);
    },
    leaveOut: function(event) {
      if (!event.target.contains(event.relatedTarget)) {
        this.leave(event);
      }
      this.out(event);
    },
    enterOver: function(event) {
      if (!event.target.contains(event.relatedTarget)) {
        this.enter(event);
      }
      this.over(event);
    },
    // LISTENER LOGIC
    eventHandler: function(inEvent) {
      // This is used to prevent multiple dispatch of pointerevents from
      // platform events. This can happen when two elements in different scopes
      // are set up to create pointer events, which is relevant to Shadow DOM.
      if (this.handledEvents.get(inEvent)) {
        return;
      }
      var type = inEvent.type;
      var fn = this.eventMap && this.eventMap[type];
      if (fn) {
        fn(inEvent);
      }
      this.handledEvents.set(inEvent, true);
    },
    // set up event listeners
    listen: function(target, events) {
      events.forEach(function(e) {
        this.addEvent(target, e);
      }, this);
    },
    // remove event listeners
    unlisten: function(target, events) {
      events.forEach(function(e) {
        this.removeEvent(target, e);
      }, this);
    },
    addEvent: function(target, eventName) {
      target.addEventListener(eventName, this.boundHandler);
    },
    removeEvent: function(target, eventName) {
      target.removeEventListener(eventName, this.boundHandler);
    },
    // EVENT CREATION AND TRACKING
    /**
     * Creates a new Event of type `inType`, based on the information in
     * `inEvent`.
     *
     * @param {string} inType A string representing the type of event to create
     * @param {Event} inEvent A platform event with a target
     * @return {Event} A PointerEvent of type `inType`
     */
    makeEvent: function(inType, inEvent) {
      var e = new PointerEvent(inType, inEvent);
      this.targets.set(e, this.targets.get(inEvent) || inEvent.target);
      return e;
    },
    // make and dispatch an event in one call
    fireEvent: function(inType, inEvent) {
      var e = this.makeEvent(inType, inEvent);
      return this.dispatchEvent(e);
    },
    /**
     * Returns a snapshot of inEvent, with writable properties.
     *
     * @param {Event} inEvent An event that contains properties to copy.
     * @return {Object} An object containing shallow copies of `inEvent`'s
     *    properties.
     */
    cloneEvent: function(inEvent) {
      var eventCopy = {};
      for (var n in inEvent) {
        eventCopy[n] = inEvent[n];
      }
      return eventCopy;
    },
    getTarget: function(inEvent) {
      // if pointer capture is set, route all events for the specified pointerId
      // to the capture target
      if (this.captureInfo) {
        if (this.captureInfo.id === inEvent.pointerId) {
          return this.captureInfo.target;
        }
      }
      return this.targets.get(inEvent);
    },
    setCapture: function(inPointerId, inTarget) {
      if (this.captureInfo) {
        this.releaseCapture(this.captureInfo.id);
      }
      this.captureInfo = {id: inPointerId, target: inTarget};
      var e = new PointerEvent('gotpointercapture', { bubbles: true });
      this.implicitRelease = this.releaseCapture.bind(this, inPointerId);
      document.addEventListener('pointerup', this.implicitRelease);
      document.addEventListener('pointercancel', this.implicitRelease);
      this.targets.set(e, inTarget);
      this.asyncDispatchEvent(e);
    },
    releaseCapture: function(inPointerId) {
      if (this.captureInfo && this.captureInfo.id === inPointerId) {
        var e = new PointerEvent('lostpointercapture', { bubbles: true });
        var t = this.captureInfo.target;
        this.captureInfo = null;
        document.removeEventListener('pointerup', this.implicitRelease);
        document.removeEventListener('pointercancel', this.implicitRelease);
        this.targets.set(e, t);
        this.asyncDispatchEvent(e);
      }
    },
    /**
     * Dispatches the event to its target.
     *
     * @param {Event} inEvent The event to be dispatched.
     * @return {Boolean} True if an event handler returns true, false otherwise.
     */
    dispatchEvent: function(inEvent) {
      var t = this.getTarget(inEvent);
      if (t) {
        return t.dispatchEvent(inEvent);
      }
    },
    asyncDispatchEvent: function(inEvent) {
      setTimeout(this.dispatchEvent.bind(this, inEvent), 0);
    },
    scrollTypes: {
      EMITTER: 'none',
      XSCROLLER: 'pan-x',
      YSCROLLER: 'pan-y',
      SCROLLER: /^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/,
    },
    touchActionToScrollType: function(inTouchAction) {
      var t = inTouchAction;
      if (t === 'none') {
        return 'none';
      } else if (t === this.XSCROLLER) {
        return 'X';
      } else if (t === this.YSCROLLER) {
        return 'Y';
      } else if (this.SCROLLER.exec(t)) {
        return 'XY';
      }
    },
    setTouchAction: function(target, touchAction) {
      var st = this.touchActionToScrollType(touchAction);
      if (target.setAttribute) {
        target[(st ? 'set' : 'remove') + 'Attribute']('touch-action', st);
      }
      this.scrollType[st ? 'set' : 'delete'](target, st);
    }
  };
  dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
  scope.dispatcher = dispatcher;
  scope.register = dispatcher.register.bind(scope);
  scope.unregister = dispatcher.unregister.bind(scope);
  scope.setTouchAction = dispatcher.setTouchAction.bind(dispatcher);
})(window.PointerEventsPolyfill);
<commit_msg>fix binding for register, move scrolltypes about<commit_after>/*
 * Copyright 2013 The Polymer Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

(function(scope) {

  /**
   * This module is for normalizing events. Mouse and Touch events will be
   * collected here, and fire PointerEvents that have the same semantics, no
   * matter the source.
   * Events fired:
   *   - pointerdown: a pointing is added
   *   - pointerup: a pointer is removed
   *   - pointermove: a pointer is moved
   *   - pointerover: a pointer crosses into an element
   *   - pointerout: a pointer leaves an element
   *   - pointercancel: a pointer will no longer generate events
   */
  var dispatcher = {
    targets: new scope.SideTable,
    handledEvents: new scope.SideTable,
    scrollType: new scope.SideTable,
    pointermap: new scope.PointerMap,
    eventMap: {},
    // Scope objects for native events.
    // This exists for ease of testing.
    eventSources: {},
    eventSourceList: [],
    scrollTypes: {
      EMITTER: 'none',
      XSCROLLER: 'pan-x',
      YSCROLLER: 'pan-y',
      SCROLLER: /^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/,
    },
    /**
     * Add a new event source that will generate pointer events.
     *
     * `inSource` must contain an array of event names named `events`, and
     * functions with the names specified in the `events` array.
     * @param {string} name A name for the event source
     * @param {Object} source A new source of platform events.
     */
    registerSource: function(name, source) {
      var s = source;
      var newEvents = s.events;
      if (newEvents) {
        newEvents.forEach(function(e) {
          if (s[e]) {
            this.eventMap[e] = s[e].bind(s);
          }
        }, this);
        this.eventSources[name] = s;
        this.eventSourceList.push(s);
      }
    },
    register: function(element) {
      var l = this.eventSourceList.length;
      for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
        // call eventsource register
        es.register.call(es, element);
      }
    },
    unregister: function(element) {
      var l = this.eventSourceList.length;
      for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
        // call eventsource register
        es.unregister.call(es, element);
      }
    },
    // EVENTS
    down: function(inEvent) {
      this.fireEvent('pointerdown', inEvent)
    },
    move: function(inEvent) {
      this.fireEvent('pointermove', inEvent);
    },
    up: function(inEvent) {
      this.fireEvent('pointerup', inEvent);
    },
    enter: function(inEvent) {
      inEvent.bubbles = false;
      this.fireEvent('pointerenter', inEvent)
    },
    leave: function(inEvent) {
      inEvent.bubbles = false;
      this.fireEvent('pointerleave', inEvent);
    },
    over: function(inEvent) {
      inEvent.bubbles = true;
      this.fireEvent('pointerover', inEvent)
    },
    out: function(inEvent) {
      inEvent.bubbles = true;
      this.fireEvent('pointerout', inEvent);
    },
    cancel: function(inEvent) {
      this.fireEvent('pointercancel', inEvent);
    },
    leaveOut: function(event) {
      if (!event.target.contains(event.relatedTarget)) {
        this.leave(event);
      }
      this.out(event);
    },
    enterOver: function(event) {
      if (!event.target.contains(event.relatedTarget)) {
        this.enter(event);
      }
      this.over(event);
    },
    // LISTENER LOGIC
    eventHandler: function(inEvent) {
      // This is used to prevent multiple dispatch of pointerevents from
      // platform events. This can happen when two elements in different scopes
      // are set up to create pointer events, which is relevant to Shadow DOM.
      if (this.handledEvents.get(inEvent)) {
        return;
      }
      var type = inEvent.type;
      var fn = this.eventMap && this.eventMap[type];
      if (fn) {
        fn(inEvent);
      }
      this.handledEvents.set(inEvent, true);
    },
    // set up event listeners
    listen: function(target, events) {
      events.forEach(function(e) {
        this.addEvent(target, e);
      }, this);
    },
    // remove event listeners
    unlisten: function(target, events) {
      events.forEach(function(e) {
        this.removeEvent(target, e);
      }, this);
    },
    addEvent: function(target, eventName) {
      target.addEventListener(eventName, this.boundHandler);
    },
    removeEvent: function(target, eventName) {
      target.removeEventListener(eventName, this.boundHandler);
    },
    // EVENT CREATION AND TRACKING
    /**
     * Creates a new Event of type `inType`, based on the information in
     * `inEvent`.
     *
     * @param {string} inType A string representing the type of event to create
     * @param {Event} inEvent A platform event with a target
     * @return {Event} A PointerEvent of type `inType`
     */
    makeEvent: function(inType, inEvent) {
      var e = new PointerEvent(inType, inEvent);
      this.targets.set(e, this.targets.get(inEvent) || inEvent.target);
      return e;
    },
    // make and dispatch an event in one call
    fireEvent: function(inType, inEvent) {
      var e = this.makeEvent(inType, inEvent);
      return this.dispatchEvent(e);
    },
    /**
     * Returns a snapshot of inEvent, with writable properties.
     *
     * @param {Event} inEvent An event that contains properties to copy.
     * @return {Object} An object containing shallow copies of `inEvent`'s
     *    properties.
     */
    cloneEvent: function(inEvent) {
      var eventCopy = {};
      for (var n in inEvent) {
        eventCopy[n] = inEvent[n];
      }
      return eventCopy;
    },
    getTarget: function(inEvent) {
      // if pointer capture is set, route all events for the specified pointerId
      // to the capture target
      if (this.captureInfo) {
        if (this.captureInfo.id === inEvent.pointerId) {
          return this.captureInfo.target;
        }
      }
      return this.targets.get(inEvent);
    },
    setCapture: function(inPointerId, inTarget) {
      if (this.captureInfo) {
        this.releaseCapture(this.captureInfo.id);
      }
      this.captureInfo = {id: inPointerId, target: inTarget};
      var e = new PointerEvent('gotpointercapture', { bubbles: true });
      this.implicitRelease = this.releaseCapture.bind(this, inPointerId);
      document.addEventListener('pointerup', this.implicitRelease);
      document.addEventListener('pointercancel', this.implicitRelease);
      this.targets.set(e, inTarget);
      this.asyncDispatchEvent(e);
    },
    releaseCapture: function(inPointerId) {
      if (this.captureInfo && this.captureInfo.id === inPointerId) {
        var e = new PointerEvent('lostpointercapture', { bubbles: true });
        var t = this.captureInfo.target;
        this.captureInfo = null;
        document.removeEventListener('pointerup', this.implicitRelease);
        document.removeEventListener('pointercancel', this.implicitRelease);
        this.targets.set(e, t);
        this.asyncDispatchEvent(e);
      }
    },
    /**
     * Dispatches the event to its target.
     *
     * @param {Event} inEvent The event to be dispatched.
     * @return {Boolean} True if an event handler returns true, false otherwise.
     */
    dispatchEvent: function(inEvent) {
      var t = this.getTarget(inEvent);
      if (t) {
        return t.dispatchEvent(inEvent);
      }
    },
    asyncDispatchEvent: function(inEvent) {
      setTimeout(this.dispatchEvent.bind(this, inEvent), 0);
    },
    touchActionToScrollType: function(inTouchAction) {
      var t = inTouchAction;
      var st = this.scrollTypes;
      if (t === 'none') {
        return 'none';
      } else if (t === st.XSCROLLER) {
        return 'X';
      } else if (t === st.YSCROLLER) {
        return 'Y';
      } else if (st.SCROLLER.exec(t)) {
        return 'XY';
      }
    },
    setTouchAction: function(target, touchAction) {
      var st = this.touchActionToScrollType(touchAction);
      if (target.setAttribute) {
        target[(st ? 'set' : 'remove') + 'Attribute']('touch-action', st);
      }
      this.scrollType[st ? 'set' : 'delete'](target, st);
    }
  };
  dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
  scope.dispatcher = dispatcher;
  scope.register = dispatcher.register.bind(dispatcher);
  scope.unregister = dispatcher.unregister.bind(dispatcher);
  scope.setTouchAction = dispatcher.setTouchAction.bind(dispatcher);
})(window.PointerEventsPolyfill);
<|endoftext|>


<commit_before>var Weibo = require('nodeweibo');    // require('nodeweibo') also works if you have installed nodeweibo via npm
var setting = require('./setting.json');   // get setting (appKey, appSecret, etc.)
var utility = require('zh.asoiaf.utility');
var wikia = new utility.Wikia();
var request = require('request').defaults({ encoding: null });;
var BufferList = require('bufferlist').BufferList;
var FormData = require('form-data');
var levenshtein = require('levenshtein-edit-distance');
var globalVar = {
		"debug": true,
		"MSG_NOFOUND":"没找到相关信息。",
		"MSG_ERR":"没玉米了，服务器罢工了。",
		"character_days_in_power":3,
		"character":"丹妮莉丝·坦格利安"
	};
module.exports = {
	comment: function(source, access_token, arg_query, arg_id, arg_cid){
		var query = arg_query;
		var id = arg_id;
		var cid = arg_cid;
		wikia.info({title:query}, function(err, obj){
			if((err=''||err==null) && obj != null){
				if (cid == null){
					var para = {
					    "source": source,
					    "access_token": access_token,
					    "comment": obj.abstract.substring(0,120)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + obj.id,
					    "id" : id
					}

					Weibo.Comments.create(para, function(data){
						if (globalVar.debug){
							console.log(data);
						}
					});

				}else{
					var para = {
					    "source": source,
					    "access_token": access_token,
					    "comment": obj.abstract.substring(0,110)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + obj.id,
					    "id" : id,
					    "cid" : cid
					}
					Weibo.Comments.reply(para, function(data){
						if (globalVar.debug){
							console.log(data);
						}
					});

				}

			}else{
				wikia.search(query, function(err, items) {

					var msg = null;
					if (err) {
						msg = globalVar.MSG_NOFOUND;
					} else if (!items || items.length == 0) {
						msg = globalVar.MSG_ERR
					} else {
						//it is a successful search
					}
					if (cid == null){
						var para = {
						    "source": source,
						    "access_token": access_token,
						    "comment": msg == null ? items[0].abstract.substring(0,120)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + items[0].id : msg,
						    "id" : id
						}
						if(msg != globalVar.MSG_ERR && msg != globalVar.MSG_NOFOUND && levenshtein(query, items[0].title) < 10){
							Weibo.Comments.create(para, function(data){
								if (globalVar.debug){
									console.log(data);
								}
							});
						}
					}else{
						var para = {
						    "source": source,
						    "access_token": access_token,
						    "comment": msg == null ? items[0].abstract.substring(0,110)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + items[0].id : msg,
						    "id" : id,
						    "cid" : cid
						}
						if(msg != globalVar.MSG_ERR && msg != globalVar.MSG_NOFOUND && levenshtein(query, items[0].title) < 10){
							Weibo.Comments.reply(para, function(data){
								if (globalVar.debug){
									console.log(data);
								}
							});
						}
					}
				});
			}

		});

	},
	status: function(source, access_token, content, username){
		wikia.info({title:content}, function(err, obj){
			if((err=''||err==null) && obj != null){
				var para = {
				    "source": source,
				    "access_token": access_token,
				    "status": "@"+username+" "+obj.abstract.substring(0,110)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + obj.id ,
				}
				Weibo.Statuses.update(para, function(data){
					if (globalVar.debug){
						console.log(data);
					}
				});
			}else{
				wikia.search(content, function(err, items) {
					var msg = null;
					if (err) {
						msg = globalVar.MSG_NOFOUND;
					} else if (!items || items.length == 0) {
						msg = globalVar.MSG_ERR
					} else {
						//it is a successful search
					}
					var para = {
					    "source": source,
					    "access_token": access_token,
					    "status": msg == null ? "@"+username+" "+items[0].abstract.substring(0,110)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + items[0].id : msg,
					}
					if( msg != globalVar.MSG_ERR && msg != globalVar.MSG_NOFOUND && levenshtein(query, items[0].title) < 8){

						Weibo.Statuses.update(para, function(data){
							if (globalVar.debug){
								console.log(data);
							}
						});

					}

				});
			}
		})

	},
	specialStatus: function(source, access_token, type){

		if(type == "most viewed"){
			url = "http://zh.asoiaf.wikia.com/api/v1/Articles/Top?namespaces=0&category=%E4%BA%BA%E7%89%A9&limit=1";
			request.get(url, function(err, res, body){
				if (!err && res.statusCode == 200) {

					var items = JSON.parse(body).items;
					var winner = items[0].title;
					var obj = {};

					obj.title = decodeURI(winner);
					obj.width = 600;
					obj.height = 800;
					obj['abstract'] = 120;
					if (globalVar.debug){
						console.log(obj);
					}

					wikia.info(obj, function(err, data){
						if(!err){
							var msg = "";
							var imageurl = "";

							msg = "昨日冰与火之歌中文维基最受关注的人物是";
							msg += data.title;
							if(data.title == globalVar.character){
								msg += "，这已经是";
								msg += data.title;
								msg += "连续第";
								msg += globalVar.character_days_in_power;
								msg += "天登上榜首。";
								msg += "http://zh.asoiaf.wikia.com";
								msg += "/index.php?curid=";
								msg += data.id;
								imageurl = data.thumbnail;
								globalVar.character_days_in_power++;
							}else{
								msg += "。";
								msg += data.title;
								msg += "取代了";
								msg += globalVar.character;
								msg += "登上榜首。";
								msg += "http://zh.asoiaf.wikia.com";
								msg += "/index.php?curid=";
								msg += data.id;
								imageurl = data.thumbnail;
								globalVar.character = data.title;
								globalVar.character_days_in_power = 1;
							}

							_postStatusWithImage(source, access_token, msg, imageurl);

						}
					});
				}else{
					if(globalVar.debug){
						console.log(err);
						globalVar.character_days_in_power++;
					}
				}
			});

		}
	},

	random: function(source, access_token){
		url="http://zh.asoiaf.wikia.com/api.php?action=query&list=random&rnlimit=1&format=json&rnnamespace=0|114"
		var my_source = source, my_token = access_token;

		request.get(url, function(err, res, body){
			if (!err && res.statusCode == 200) {
				var query = JSON.parse(body).query;
				var winner = query.random[0].title;
				if(globalVar.debug)
					console.log(winner);
				_statusWithImage(my_source, my_token, winner );
			}
		});
	}

}
var _statusWithImage= function(source, access_token, title){
	var obj = {};
	var msg;
	var imageurl;
	obj.title = title;
	obj.width = 600;
	obj.height = 800;
	obj['abstract'] = 500;

	wikia.info(obj, function(err, data){
		if(globalVar.debug){
			console.log(data);
		}
		if(!err){
			/* prelimiary quality check */
			if (data.abstract.length < 30 || data.abstract.indexOf('，')=='-1'|| data.type != 'article'){
				return;
			}

			msg = data.abstract.substring(0, 120);
			msg += "..."
			msg += "http://zh.asoiaf.wikia.com"
			msg += "/index.php?curid=";
			msg += data.id;
			imageurl = data.thumbnail;

			_postStatusWithImage(source, access_token, msg, imageurl);

		}
	});
}
var _postStatusWithImage = function(source, access_token, msg, imageurl){
	if (imageurl == null || imageurl == ""){
		var para = {
			"source" : source,
			"access_token" : access_token,
			"status" : msg
		}
		Weibo.Statuses.update(para, function(data){
			if (globalVar.debug){
				console.log(data);
			}

		});

	}else{
		if(globalVar.debug)
			console.log(imageurl);
	    // var bl = new BufferList();
	    var form = new FormData();
	    form.append('source',source);
	    form.append('access_token', access_token);
	    form.append('status', msg);
	    form.append('pic', request(imageurl));

		form.submit('https://api.weibo.com/2/statuses/upload.json', function(err, res) {
			if(globalVar.debug){
				console.log(res);
			}
			res.resume();
		  // res – response object (http.IncomingMessage)  //
		  // for node-0.10.x
		});
	  //   request({uri:imageurl, responseBodyStream: bl}, function (error, response, body) {
	  //       if (!error && response.statusCode == 200) {
	  //       	var form = new FormData();

		 //        var para = {
			// 		"source" : source,
			// 		"access_token" : access_token,
			// 		"status" : msg,
			// 		"pic" : image
			// 	}
			// 	Weibo.Statuses.upload(para, function(data){
			// 		if (globalVar.debug){
			// 			console.log(data);
			// 		}
			// 	});
			// }

	  //   });

	}
};<commit_msg>降低图片品质要求<commit_after>var Weibo = require('nodeweibo');    // require('nodeweibo') also works if you have installed nodeweibo via npm
var setting = require('./setting.json');   // get setting (appKey, appSecret, etc.)
var utility = require('zh.asoiaf.utility');
var wikia = new utility.Wikia();
var request = require('request').defaults({ encoding: null });;
var BufferList = require('bufferlist').BufferList;
var FormData = require('form-data');
var levenshtein = require('levenshtein-edit-distance');
var globalVar = {
		"debug": true,
		"MSG_NOFOUND":"没找到相关信息。",
		"MSG_ERR":"没玉米了，服务器罢工了。",
		"character_days_in_power":3,
		"character":"丹妮莉丝·坦格利安"
	};
module.exports = {
	comment: function(source, access_token, arg_query, arg_id, arg_cid){
		var query = arg_query;
		var id = arg_id;
		var cid = arg_cid;
		wikia.info({title:query}, function(err, obj){
			if((err=''||err==null) && obj != null){
				if (cid == null){
					var para = {
					    "source": source,
					    "access_token": access_token,
					    "comment": obj.abstract.substring(0,120)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + obj.id,
					    "id" : id
					}

					Weibo.Comments.create(para, function(data){
						if (globalVar.debug){
							console.log(data);
						}
					});

				}else{
					var para = {
					    "source": source,
					    "access_token": access_token,
					    "comment": obj.abstract.substring(0,110)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + obj.id,
					    "id" : id,
					    "cid" : cid
					}
					Weibo.Comments.reply(para, function(data){
						if (globalVar.debug){
							console.log(data);
						}
					});

				}

			}else{
				wikia.search(query, function(err, items) {

					var msg = null;
					if (err) {
						msg = globalVar.MSG_NOFOUND;
					} else if (!items || items.length == 0) {
						msg = globalVar.MSG_ERR
					} else {
						//it is a successful search
					}
					if (cid == null){
						var para = {
						    "source": source,
						    "access_token": access_token,
						    "comment": msg == null ? items[0].abstract.substring(0,120)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + items[0].id : msg,
						    "id" : id
						}
						if(msg != globalVar.MSG_ERR && msg != globalVar.MSG_NOFOUND && levenshtein(query, items[0].title) < 10){
							Weibo.Comments.create(para, function(data){
								if (globalVar.debug){
									console.log(data);
								}
							});
						}
					}else{
						var para = {
						    "source": source,
						    "access_token": access_token,
						    "comment": msg == null ? items[0].abstract.substring(0,110)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + items[0].id : msg,
						    "id" : id,
						    "cid" : cid
						}
						if(msg != globalVar.MSG_ERR && msg != globalVar.MSG_NOFOUND && levenshtein(query, items[0].title) < 10){
							Weibo.Comments.reply(para, function(data){
								if (globalVar.debug){
									console.log(data);
								}
							});
						}
					}
				});
			}

		});

	},
	status: function(source, access_token, content, username){
		wikia.info({title:content}, function(err, obj){
			if((err=''||err==null) && obj != null){
				var para = {
				    "source": source,
				    "access_token": access_token,
				    "status": "@"+username+" "+obj.abstract.substring(0,110)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + obj.id ,
				}
				Weibo.Statuses.update(para, function(data){
					if (globalVar.debug){
						console.log(data);
					}
				});
			}else{
				wikia.search(content, function(err, items) {
					var msg = null;
					if (err) {
						msg = globalVar.MSG_NOFOUND;
					} else if (!items || items.length == 0) {
						msg = globalVar.MSG_ERR
					} else {
						//it is a successful search
					}
					var para = {
					    "source": source,
					    "access_token": access_token,
					    "status": msg == null ? "@"+username+" "+items[0].abstract.substring(0,110)+"... http://zh.asoiaf.wikia.com/index.php?curid=" + items[0].id : msg,
					}
					if( msg != globalVar.MSG_ERR && msg != globalVar.MSG_NOFOUND && levenshtein(query, items[0].title) < 8){

						Weibo.Statuses.update(para, function(data){
							if (globalVar.debug){
								console.log(data);
							}
						});

					}

				});
			}
		})

	},
	specialStatus: function(source, access_token, type){

		if(type == "most viewed"){
			url = "http://zh.asoiaf.wikia.com/api/v1/Articles/Top?namespaces=0&category=%E4%BA%BA%E7%89%A9&limit=1";
			request.get(url, function(err, res, body){
				if (!err && res.statusCode == 200) {

					var items = JSON.parse(body).items;
					var winner = items[0].title;
					var obj = {};

					obj.title = decodeURI(winner);
					obj.width = 200;
					obj.height = 100;
					obj['abstract'] = 120;
					if (globalVar.debug){
						console.log(obj);
					}

					wikia.info(obj, function(err, data){
						if(!err){
							var msg = "";
							var imageurl = "";

							msg = "昨日冰与火之歌中文维基最受关注的人物是";
							msg += data.title;
							if(data.title == globalVar.character){
								msg += "，这已经是";
								msg += data.title;
								msg += "连续第";
								msg += globalVar.character_days_in_power;
								msg += "天登上榜首。";
								msg += "http://zh.asoiaf.wikia.com";
								msg += "/index.php?curid=";
								msg += data.id;
								imageurl = data.thumbnail;
								globalVar.character_days_in_power++;
							}else{
								msg += "。";
								msg += data.title;
								msg += "取代了";
								msg += globalVar.character;
								msg += "登上榜首。";
								msg += "http://zh.asoiaf.wikia.com";
								msg += "/index.php?curid=";
								msg += data.id;
								imageurl = data.thumbnail;
								globalVar.character = data.title;
								globalVar.character_days_in_power = 1;
							}

							_postStatusWithImage(source, access_token, msg, imageurl);

						}
					});
				}else{
					if(globalVar.debug){
						console.log(err);
						globalVar.character_days_in_power++;
					}
				}
			});

		}
	},

	random: function(source, access_token){
		url="http://zh.asoiaf.wikia.com/api.php?action=query&list=random&rnlimit=1&format=json&rnnamespace=0|114"
		var my_source = source, my_token = access_token;

		request.get(url, function(err, res, body){
			if (!err && res.statusCode == 200) {
				var query = JSON.parse(body).query;
				var winner = query.random[0].title;
				if(globalVar.debug)
					console.log(winner);
				_statusWithImage(my_source, my_token, winner );
			}
		});
	}

}
var _statusWithImage= function(source, access_token, title){
	var obj = {};
	var msg;
	var imageurl;
	obj.title = title;
	obj.width = 200;
	obj.height = 100;
	obj['abstract'] = 500;

	wikia.info(obj, function(err, data){
		if(globalVar.debug){
			console.log(data);
		}
		if(!err){
			/* prelimiary quality check */
			if (data.abstract.length < 30 || data.abstract.indexOf('，')=='-1'|| data.type != 'article'){
				return;
			}

			msg = data.abstract.substring(0, 120);
			msg += "..."
			msg += "http://zh.asoiaf.wikia.com"
			msg += "/index.php?curid=";
			msg += data.id;
			imageurl = data.thumbnail;

			_postStatusWithImage(source, access_token, msg, imageurl);

		}
	});
}
var _postStatusWithImage = function(source, access_token, msg, imageurl){
	if (imageurl == null || imageurl == ""){
		var para = {
			"source" : source,
			"access_token" : access_token,
			"status" : msg
		}
		Weibo.Statuses.update(para, function(data){
			if (globalVar.debug){
				console.log(data);
			}

		});

	}else{
		if(globalVar.debug)
			console.log(imageurl);
	    // var bl = new BufferList();
	    var form = new FormData();
	    form.append('source',source);
	    form.append('access_token', access_token);
	    form.append('status', msg);
	    form.append('pic', request(imageurl));

		form.submit('https://api.weibo.com/2/statuses/upload.json', function(err, res) {
			if(globalVar.debug){
				console.log(res);
			}
			res.resume();
		  // res – response object (http.IncomingMessage)  //
		  // for node-0.10.x
		});
	  //   request({uri:imageurl, responseBodyStream: bl}, function (error, response, body) {
	  //       if (!error && response.statusCode == 200) {
	  //       	var form = new FormData();

		 //        var para = {
			// 		"source" : source,
			// 		"access_token" : access_token,
			// 		"status" : msg,
			// 		"pic" : image
			// 	}
			// 	Weibo.Statuses.upload(para, function(data){
			// 		if (globalVar.debug){
			// 			console.log(data);
			// 		}
			// 	});
			// }

	  //   });

	}
};<|endoftext|>


<commit_before>import _ from "../util/index";
import { LEGACY_IE, WEBKIT_PREFIX, WINDOW, DOCUMENT, CUSTOM_EVENT_TYPE } from "../const";
import { StaticMethodError } from "../errors";
import { $Element, DOM } from "../types";
import importStyles from "./importstyles";
import ExtensionHandler from "../util/extensionhandler";

// Inspired by trick discovered by Daniel Buchner:
// https://github.com/csuwldcat/SelectorListener

var extensions = [],
    returnTrue = () => true,
    returnFalse = () => false,
    ANIMATION_ID = "DOM" + Date.now(),
    EVENT_TYPE = WEBKIT_PREFIX ? "webkitAnimationEnd" : "animationend",
    readyCallback, styles;

if (LEGACY_IE) {
    let legacyScripts = _.filter.call(DOCUMENT.scripts, (script) => script.src.indexOf("better-dom-legacy.js") >= 0);

    if (legacyScripts.length < 1) {
        throw new Error("In order to use live extensions in IE < 10 you have to include extra files. See <%= pkg.repository.url %>#notes-about-old-ies for details.");
    }

    styles = {behavior: "url(" + legacyScripts[0].src.replace(".js", ".htc") + ") !important"};

    DOCUMENT.attachEvent("on" + CUSTOM_EVENT_TYPE, () => {
        var e = WINDOW.event;

        if (e.srcUrn === CUSTOM_EVENT_TYPE) {
            extensions.forEach(ExtensionHandler.traverse(e.srcElement));
        }
    });
} else {
    let readyState = DOCUMENT.readyState;
    // IE10 and lower don't handle "interactive" properly... use a weak inference to detect it
    // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
    if (DOCUMENT.attachEvent ? readyState !== "complete" : readyState === "loading") {
        readyCallback = () => {
            // MUST check for the readyCallback to avoid double
            // initialization on window.onload event
            if (readyCallback) {
                extensions.forEach((ext) => { ext.start() });

                readyCallback = null;
            }
        };

        // use DOMContentLoaded to initialize any live extension
        // AFTER the document is completely parsed to avoid quirks
        DOCUMENT.addEventListener("DOMContentLoaded", readyCallback, false);
        // just in case the DOMContentLoaded event fails use onload
        WINDOW.addEventListener("load", readyCallback, false);
    }

    importStyles("@" + WEBKIT_PREFIX + "keyframes " + ANIMATION_ID, "from {opacity:.99} to {opacity:1}");

    styles = {
        "animation-duration": "1ms !important",
        "animation-name": ANIMATION_ID + " !important"
    };

    DOCUMENT.addEventListener(EVENT_TYPE, (e) => {
        if (e.animationName === ANIMATION_ID) {
            extensions.forEach(ExtensionHandler.traverse(e.target));
            // this is an internal event - stop it immediately
            e.stopImmediatePropagation();
        }
    }, true);
}

/**
 * Declare a live extension
 * @memberof DOM
 * @alias DOM.extend
 * @param  {String}           selector         css selector of which elements to capture
 * @param  {Boolean|Function} [condition=true] indicates if live extension should be attached or not
 * @param  {Object}           mixins           extension declatation
 * @see https://github.com/chemerisuk/better-dom/wiki/Live-extensions
 * @example
 * DOM.extend("selector", {
 *     constructor: function() {
 *         // initialize component
 *     },
 *     publicMethod: function() {
 *         // ...
 *     }
 * });
 */
DOM.extend = function(selector, condition, mixins) {
    if (arguments.length === 2) {
        mixins = condition;
        condition = true;
    }

    if (typeof condition === "boolean") condition = condition ? returnTrue : returnFalse;
    if (typeof mixins === "function") mixins = {constructor: mixins};

    if (!mixins || typeof mixins !== "object" || typeof condition !== "function") throw new StaticMethodError("extend", arguments);

    if (selector === "*") {
        // extending element prototype
        _.assign($Element.prototype, mixins);
    } else {
        var ext = ExtensionHandler(selector, condition, mixins, extensions.length);

        ext.start = () => {
            // initialize extension manually to make sure that all elements
            // have appropriate methods before they are used in other DOM.extend.
            // Also fixes legacy IEs when the HTC behavior is already attached
            _.each.call(DOCUMENT.querySelectorAll(selector), ext);
            // MUST be after querySelectorAll because of legacy IEs quirks
            DOM.importStyles(selector, styles);
        };

        extensions.push(ext);

        if (!readyCallback) ext.start();
    }
};

export default extensions;
<commit_msg>use event animationstart in DOM.extend<commit_after>import _ from "../util/index";
import { LEGACY_IE, WEBKIT_PREFIX, WINDOW, DOCUMENT, CUSTOM_EVENT_TYPE } from "../const";
import { StaticMethodError } from "../errors";
import { $Element, DOM } from "../types";
import importStyles from "./importstyles";
import ExtensionHandler from "../util/extensionhandler";

// Inspired by trick discovered by Daniel Buchner:
// https://github.com/csuwldcat/SelectorListener

var extensions = [],
    returnTrue = () => true,
    returnFalse = () => false,
    readyCallback, styles;

if (LEGACY_IE) {
    let legacyScripts = _.filter.call(DOCUMENT.scripts, (script) => script.src.indexOf("better-dom-legacy.js") >= 0);

    if (legacyScripts.length < 1) {
        throw new Error("In order to use live extensions in IE < 10 you have to include extra files. See <%= pkg.repository.url %>#notes-about-old-ies for details.");
    }

    styles = {behavior: "url(" + legacyScripts[0].src.replace(".js", ".htc") + ") !important"};

    DOCUMENT.attachEvent("on" + CUSTOM_EVENT_TYPE, () => {
        var e = WINDOW.event;

        if (e.srcUrn === CUSTOM_EVENT_TYPE) {
            extensions.forEach(ExtensionHandler.traverse(e.srcElement));
        }
    });
} else {
    let ANIMATION_ID = "DOM" + Date.now();
    let readyState = DOCUMENT.readyState;
    // IE10 and lower don't handle "interactive" properly... use a weak inference to detect it
    // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
    if (DOCUMENT.attachEvent ? readyState !== "complete" : readyState === "loading") {
        readyCallback = () => {
            // MUST check for the readyCallback to avoid double
            // initialization on window.onload event
            if (readyCallback) {
                extensions.forEach((ext) => { ext.start() });

                readyCallback = null;
            }
        };

        // use DOMContentLoaded to initialize any live extension
        // AFTER the document is completely parsed to avoid quirks
        DOCUMENT.addEventListener("DOMContentLoaded", readyCallback, false);
        // just in case the DOMContentLoaded event fails use onload
        WINDOW.addEventListener("load", readyCallback, false);
    }

    importStyles("@" + WEBKIT_PREFIX + "keyframes " + ANIMATION_ID, "from {opacity:.99} to {opacity:1}");

    styles = {
        "animation-duration": "1ms !important",
        "animation-name": ANIMATION_ID + " !important"
    };

    DOCUMENT.addEventListener(WEBKIT_PREFIX ? "webkitAnimationStart" : "animationstart", (e) => {
        if (e.animationName === ANIMATION_ID) {
            extensions.forEach(ExtensionHandler.traverse(e.target));
            // this is an internal event - stop it immediately
            e.stopImmediatePropagation();
        }
    }, true);
}

/**
 * Declare a live extension
 * @memberof DOM
 * @alias DOM.extend
 * @param  {String}           selector         css selector of which elements to capture
 * @param  {Boolean|Function} [condition=true] indicates if live extension should be attached or not
 * @param  {Object}           mixins           extension declatation
 * @see https://github.com/chemerisuk/better-dom/wiki/Live-extensions
 * @example
 * DOM.extend("selector", {
 *     constructor: function() {
 *         // initialize component
 *     },
 *     publicMethod: function() {
 *         // ...
 *     }
 * });
 */
DOM.extend = function(selector, condition, mixins) {
    if (arguments.length === 2) {
        mixins = condition;
        condition = true;
    }

    if (typeof condition === "boolean") condition = condition ? returnTrue : returnFalse;
    if (typeof mixins === "function") mixins = {constructor: mixins};

    if (!mixins || typeof mixins !== "object" || typeof condition !== "function") throw new StaticMethodError("extend", arguments);

    if (selector === "*") {
        // extending element prototype
        _.assign($Element.prototype, mixins);
    } else {
        var ext = ExtensionHandler(selector, condition, mixins, extensions.length);

        ext.start = () => {
            // initialize extension manually to make sure that all elements
            // have appropriate methods before they are used in other DOM.extend.
            // Also fixes legacy IEs when the HTC behavior is already attached
            _.each.call(DOCUMENT.querySelectorAll(selector), ext);
            // MUST be after querySelectorAll because of legacy IEs quirks
            DOM.importStyles(selector, styles);
        };

        extensions.push(ext);

        if (!readyCallback) ext.start();
    }
};

export default extensions;
<|endoftext|>


<commit_before>describe('test', function () {
    beforeEach(function () {
        this.browserMusicPlayer = new BrowserMusicPlayer();
    });

    it('play sound', function () {
        assert();
    });

    it('stop sound', function () {
        assert();
    });
});
<commit_msg>Add tests<commit_after>describe('Music player in browser', function () {
    this.browserMusicPlayer = new BrowserMusicPlayer();

    describe('#loadMusic()', function () {
        it('should load music file', function () {
            assert(0 === 1);
        });
    });

    describe('#isMusicFile()', function () {
        it('should is music file', function () {
            assert(0 === 1);
        });
    });

    describe('#play()', function () {
        it('should play sound', function () {
            assert(0 === 1);
        });
    });

    describe('#stop()', function () {
        it('should stop sound', function () {
            assert(0 === 1);
        });
    });

    describe('#rewind()', function () {
        it('should rewind sound', function () {
            assert(0 === 1);
        });
    });

    describe('#forward()', function () {
        it('should forward sound', function () {
            assert(0 === 1);
        });
    });
});
<|endoftext|>


<commit_before>function Address (legislator) {
    this._legislator = legislator
    this.spigot = new Spigot(this)
    this.basin = new Basin(this)
}

Address.prototype.request = cadence(function (async, envelope) {
    return {
        to: this._legislator.properties[envelope.body.to],
        from: envelope.from,
        body: envelope.body
    }
})

Address.prototype.response = cadence(function (async, envelope) {
    return envelope
})
<commit_msg>Remove dead code.<commit_after><|endoftext|>


<commit_before>
	function getParamFunc(getParam, setParam, obj){
		return function(data, widthBaseParam){
			if (isPlainObject(data)) {
				setParam(data, widthBaseParam);
				return obj;
			} else {
				return getParam(data, widthBaseParam);
			}
		};
	}
	function getRunParamFunc(run, base, paramFunc){
		extendDeep(paramFunc, base);
		extend(paramFunc, run);
		return paramFunc;
	}

	function setDisabledCall(funcData){
		funcData.disabled = true;
	}

	function findFuncInList(func, list, callback){
		forEach(list, function(funcData){
			if (funcData.func === func) callback(funcData);
		});
	}

	function returnFalseFunc(){
		return false;
	}

	return function(defaultCall, _obj){		// obj 仅仅作为方法返回值及call apply作用域使用 无特殊作用
		var _before = [],
			_after = [],
			_final = [],
			_baseParam = {},
			_runParam = {};

		function getList(stepName) {
			return stepName == 'after' ? _after : stepName == 'final' ? _final : _before;
		}
		function addEventListener(stepName, data, func){
			if (!func) {
				func = data;
				data = {};
			}
			if (!func) {
				func = stepName;
				stepName = '';
			}

			var funcData = {
				data: data,
				func: func,
				disabled: false
			};

			getList(stepName).push(funcData);

			return funcData;
		}
		function setParam(data, widthBaseParam){
			if (widthBaseParam) extend(_baseParam, data);
			extend(_runParam, data);
		}
		function getParam(name, isBaseParam){
			return isBaseParam ? _baseParam[name] : _runParam[name];
		}
		function removeParam(name, widthBaseParam) {
			if (widthBaseParam) delete _baseParam[name];
			delete _runParam[name];
		}

		return {
			'on': function(){
				addEventListener.apply(null, arguments);
				return _obj;
			},
			'off': function(stepName, func){
				if (stepName && func) {
					findFuncInList(func, getList(stepName), setDisabledCall);
				} else if (stepName) {
					if (typeof(stepName) == 'function') {
						findFuncInList(stepName, _before, setDisabledCall);
						findFuncInList(stepName, _after, setDisabledCall);
						findFuncInList(stepName, _final, setDisabledCall);
					} else {
						forEach(getList(stepName), setDisabledCall);
					}
				} else {
					forEach(_before, setDisabledCall);
					forEach(_after, setDisabledCall);
					forEach(_final, setDisabledCall);
				}

				return _obj;
			},
			'once': function(){
				var funcData = addEventListener.apply(null, arguments);
				addEventListener('final', function(){
					funcData.disabled = true;
				});
			},
			'param': getParamFunc(getParam, setParam, _obj),
			'removeParam': removeParam,
			'trigger': function(){
				if (defaultCall) return defaultCall.apply(_obj, arguments);
			},
			'emit': function(){
				var args = toArray(arguments),
					defCall = defaultCall,
					curIndex, curFuncData, curList,

					isStop = false,
					isDefaultPrevented = false,
					runList = function(list, startIndex){
						curList = list;
						for (curIndex = startIndex; !isStop && curIndex < list.length; curIndex++) {
							curFuncData = list[curIndex];
							if (curFuncData.disabled) {
								list.splice(curIndex, 1);
								curIndex--;
								continue;
							}

							args[0] = new Event();

							if ((EventProto['preReturn'] = curFuncData.func.apply(_obj, args)) === false) {
								isStop = true;
								return false;
							}

							if (isInAsync) return false;
						}

						return true;
					},
					setDefReturn = function(defReturn){
						EventProto['defReturn'] = defReturn;
						return true;
					},
					isInAsync = false,
					hasRunAfter = false,
					runAfter = function(){
						hasRunAfter = true;
						EventProto['preventDefault'] = returnFalseFunc;
						EventProto['overrideDefault'] = returnFalseFunc;
						// defaultCall run
						if (!isStop && !isDefaultPrevented) {
							EventProto['defReturn'] = EventProto['preReturn'] = defCall ? defCall.apply(_obj, arguments) : undefined;
						}

						EventProto['setDefReturn'] = setDefReturn;

						// after list run
						if (!isDefaultPrevented) runList(_after, 0);
					},
					hasRunFinal = false,
					runFinal = function(){
						// final list run
						isStop = false;		// 为final 重置isStop
						runList(_final, 0);
					},
					myRunParam = getRunParamFunc(_runParam, _baseParam, getParamFunc(getParam, setParam, _obj)),
					Event = function(){
						this['data'] = curFuncData.data;
					},
					EventProto = Event.prototype = {
						'isDefaultPrevented': false,
						'isDefaultOverrided': false,
						'isInAsync': false,
						'param': myRunParam,
						'removeParam': removeParam,
						'setDefReturn': returnFalseFunc,
						'off': function(){
							curFuncData.disabled = true;
						},
						'next': function(){			// 调用next只可能返回两种值 true 和 false
							return runList(curList, ++curIndex);
						},
						'preventDefault': function(defReturn){
							isDefaultPrevented = true;
							EventProto['isDefaultPrevented'] = true;
							EventProto['preventDefault'] = EventProto['setDefReturn'] = setDefReturn;
							EventProto['overrideDefault'] = returnFalseFunc;

							return setDefReturn(defReturn);
						},
						'overrideDefault': function(newDefaultReturn){
							defCall = newDefaultReturn;
							EventProto['isDefaultOverrided'] = true;
							EventProto['setDefReturn'] = setDefReturn;
							return true;
						},
						'async': function(){
							isInAsync = true;
							EventProto['isInAsync'] = true;

							return function(){
								if (!isInAsync) {
									isInAsync = false;
									EventProto['isInAsync'] = false;

									runList(curList, ++curIndex);
									if (!hasRunAfter) runAfter();
									if (!hasRunFinal) runFinal();
								}

								if (!isInAsync) return EventProto['defReturn'];
							};
						}
					};

				_runParam = {};			// 清空 防止影响到内部的嵌套调用

				args.unshift(null);			// Event placeholder

				// before list run
				runList(_before, 0);

				if (!isInAsync) runAfter();
				if (!isInAsync) runFinal();

				if (!isInAsync) return EventProto['defReturn'];
			}
		};
	};<commit_msg>`Event` rename `off` to `offSelf` and add `on` `off` `once` method.<commit_after>
	function getParamFunc(getParam, setParam, obj){
		return function(data, widthBaseParam){
			if (isPlainObject(data)) {
				setParam(data, widthBaseParam);
				return obj;
			} else {
				return getParam(data, widthBaseParam);
			}
		};
	}
	function getRunParamFunc(run, base, paramFunc){
		extendDeep(paramFunc, base);
		extend(paramFunc, run);
		return paramFunc;
	}

	function setDisabledCall(funcData){
		funcData.disabled = true;
	}

	function findFuncInList(func, list, callback){
		forEach(list, function(funcData){
			if (funcData.func === func) callback(funcData);
		});
	}

	function returnFalseFunc(){
		return false;
	}

	return function(defaultCall, _obj){		// obj 仅仅作为方法返回值及call apply作用域使用 无特殊作用
		var _before = [],
			_after = [],
			_final = [],
			_baseParam = {},
			_runParam = {};

		function getList(stepName) {
			return stepName == 'after' ? _after : stepName == 'final' ? _final : _before;
		}
		function addEventListener(stepName, data, func){
			if (!func) {
				func = data;
				data = {};
			}
			if (!func) {
				func = stepName;
				stepName = '';
			}

			var funcData = {
				data: data,
				func: func,
				disabled: false
			};

			getList(stepName).push(funcData);

			return funcData;
		}
		function setParam(data, widthBaseParam){
			if (widthBaseParam) extend(_baseParam, data);
			extend(_runParam, data);
		}
		function getParam(name, isBaseParam){
			return isBaseParam ? _baseParam[name] : _runParam[name];
		}
		function removeParam(name, widthBaseParam) {
			if (widthBaseParam) delete _baseParam[name];
			delete _runParam[name];
		}

		function on(){
			addEventListener.apply(null, arguments);
			return _obj;
		}
		function off(stepName, func){
			if (stepName && func) {
				findFuncInList(func, getList(stepName), setDisabledCall);
			} else if (stepName) {
				if (typeof(stepName) == 'function') {
					findFuncInList(stepName, _before, setDisabledCall);
					findFuncInList(stepName, _after, setDisabledCall);
					findFuncInList(stepName, _final, setDisabledCall);
				} else {
					forEach(getList(stepName), setDisabledCall);
				}
			} else {
				forEach(_before, setDisabledCall);
				forEach(_after, setDisabledCall);
				forEach(_final, setDisabledCall);
			}

			return _obj;
		}
		function once(){
			var funcData = addEventListener.apply(null, arguments);
			addEventListener('final', function(){
				funcData.disabled = true;
			});
		}

		return {
			'on': on,
			'off': off,
			'once': once,
			'param': getParamFunc(getParam, setParam, _obj),
			'removeParam': removeParam,
			'trigger': function(){
				if (defaultCall) return defaultCall.apply(_obj, arguments);
			},
			'emit': function(){
				var args = toArray(arguments),
					defCall = defaultCall,
					curIndex, curFuncData, curList,

					isStop = false,
					isDefaultPrevented = false,
					runList = function(list, startIndex){
						curList = list;
						for (curIndex = startIndex; !isStop && curIndex < list.length; curIndex++) {
							curFuncData = list[curIndex];
							if (curFuncData.disabled) {
								list.splice(curIndex, 1);
								curIndex--;
								continue;
							}

							args[0] = new Event();

							if ((EventProto['preReturn'] = curFuncData.func.apply(_obj, args)) === false) {
								isStop = true;
								return false;
							}

							if (isInAsync) return false;
						}

						return true;
					},
					setDefReturn = function(defReturn){
						EventProto['defReturn'] = defReturn;
						return true;
					},
					isInAsync = false,
					hasRunAfter = false,
					runAfter = function(){
						hasRunAfter = true;
						EventProto['preventDefault'] = returnFalseFunc;
						EventProto['overrideDefault'] = returnFalseFunc;
						// defaultCall run
						if (!isStop && !isDefaultPrevented) {
							EventProto['defReturn'] = EventProto['preReturn'] = defCall ? defCall.apply(_obj, arguments) : undefined;
						}

						EventProto['setDefReturn'] = setDefReturn;

						// after list run
						if (!isDefaultPrevented) runList(_after, 0);
					},
					hasRunFinal = false,
					runFinal = function(){
						// final list run
						isStop = false;		// 为final 重置isStop
						runList(_final, 0);
					},
					myRunParam = getRunParamFunc(_runParam, _baseParam, getParamFunc(getParam, setParam, _obj)),
					Event = function(){
						this['data'] = curFuncData.data;
					},
					EventProto = Event.prototype = {
						'isDefaultPrevented': false,
						'isDefaultOverrided': false,
						'isInAsync': false,
						'on': on,
						'off': off,
						'once': once,
						'param': myRunParam,
						'removeParam': removeParam,
						'setDefReturn': returnFalseFunc,
						'offSelf': function(){
							curFuncData.disabled = true;
						},
						'next': function(){			// 调用next只可能返回两种值 true 和 false
							return runList(curList, ++curIndex);
						},
						'preventDefault': function(defReturn){
							isDefaultPrevented = true;
							EventProto['isDefaultPrevented'] = true;
							EventProto['preventDefault'] = EventProto['setDefReturn'] = setDefReturn;
							EventProto['overrideDefault'] = returnFalseFunc;

							return setDefReturn(defReturn);
						},
						'overrideDefault': function(newDefaultReturn){
							defCall = newDefaultReturn;
							EventProto['isDefaultOverrided'] = true;
							EventProto['setDefReturn'] = setDefReturn;
							return true;
						},
						'async': function(){
							isInAsync = true;
							EventProto['isInAsync'] = true;

							return function(){
								if (!isInAsync) {
									isInAsync = false;
									EventProto['isInAsync'] = false;

									runList(curList, ++curIndex);
									if (!hasRunAfter) runAfter();
									if (!hasRunFinal) runFinal();
								}

								if (!isInAsync) return EventProto['defReturn'];
							};
						}
					};

				_runParam = {};			// 清空 防止影响到内部的嵌套调用

				args.unshift(null);			// Event placeholder

				// before list run
				runList(_before, 0);

				if (!isInAsync) runAfter();
				if (!isInAsync) runFinal();

				if (!isInAsync) return EventProto['defReturn'];
			}
		};
	};<|endoftext|>