<commit_before>require('list-view/list_item_view');

var get = Ember.get, set = Ember.set,
min = Math.min, max = Math.max, floor = Math.floor,
ceil = Math.ceil;

function addContentArrayObserver() {
  var content = get(this, 'content');
  if (content) {
    content.addArrayObserver(this);
  }
}

function removeAndDestroy(object){
  this.removeObject(object);
  object.destroy();
}

function syncChildViews(){
  Ember.run.once(this, '_syncChildViews');
}

function sortByContentIndex (viewOne, viewTwo){
  return get(viewOne, 'contentIndex') - get(viewTwo, 'contentIndex');
}

function detectListItemViews(childView) {
  return Ember.ListItemView.detectInstance(childView);
}

Ember.ListViewMixin = Ember.Mixin.create({
  itemViewClass: Ember.ListItemView,
  classNames: ['ember-list-view'],
  attributeBindings: ['style'],
  scrollTop: 0,
  _lastEndingIndex: 0,
  paddingCount: 1, // One row for padding

  init: function() {
    this._super();
    addContentArrayObserver.call(this);
    this._syncChildViews();
    this.columnCountDidChange();
  },

  style: Ember.computed('height', 'width', function() {
    var height, width, style;

    height = get(this, 'height');
    width = get(this, 'width');

    style = "height:" + height + "px;" + 'width:' + width  + 'px;';

    return style;
  }),

  didInsertElement: function() {
    var self, element;

    self = this,
    element = get(this, 'element');

    self._scroll = function(e) { self.scroll(e); };
    self._touchMove = function(e) { self.touchMove(e); };
    self._mouseWheel = function(e) { self.mouseWheel(e); };

    element.addEventListener('scroll',     this._scroll);
    element.addEventListener('touchmove',  this._touchMove);
    element.addEventListener('mousewheel', this._mouseWheel);
  },

  touchMove: Ember.K,
  mouseWheel: Ember.K,

  willDestroyElement: function() {
    var element;

    element = get(this, 'element');

    element.removeEventListener('scroll', this._scroll);
    element.removeEventListener('touchmove', this._touchMove);
    element.removeEventListener('mousewheel', this._mouseWheel);
  },

  // Browser fires the scroll event asynchronously
  scroll: function(e) {
    Ember.run(this, this.scrollTo, e.target.scrollTop);
  },

  scrollTo: function(scrollTop, options) {
    var contentLength, childViews, childViewsLength,
        startingIndex, endingIndex, childView, attrs,
        contentIndex, visibleEndingIndex, maxContentIndex,
        contentIndexEnd;

    options = options || { };

    set(this, 'scrollTop', scrollTop);
    contentLength = get(this, 'content.length');
    maxContentIndex = max(contentLength - 1, 0);
    childViews = get(this, 'listItemViews');
    childViewsLength =  childViews.length;

    startingIndex = this._startingIndex();
    visibleEndingIndex = startingIndex + this._numChildViewsForViewport();

    endingIndex = min(maxContentIndex, visibleEndingIndex);

    if (!options.force && startingIndex === this._lastStartingIndex && endingIndex === this._lastEndingIndex) {
      return;
    }

    contentIndexEnd = min(visibleEndingIndex, startingIndex + childViewsLength);

    for (contentIndex = startingIndex; contentIndex < contentIndexEnd; contentIndex++) {
      childView = childViews[contentIndex % childViewsLength];
      this._reuseChildForContentIndex(childView, contentIndex, options);
    }

    this._lastStartingIndex = startingIndex;
    this._lastEndingIndex = endingIndex;
  },

  childViewsWillSync: Ember.K,
  childViewsDidSync: Ember.K,

  totalHeight: Ember.computed('content.length', 'rowHeight', 'columnCount', function() {
    var contentLength, rowHeight, columnCount;

    contentLength = get(this, 'content.length');
    rowHeight = get(this, 'rowHeight');
    columnCount = get(this, 'columnCount');

    return (ceil(contentLength / columnCount)) * rowHeight;
  }),

  _prepareChildForReuse: function(childView) {
    childView.prepareForReuse();
  },

  _reuseChildForContentIndex: function(childView, contentIndex, options) {
    var content, childsCurrentContentIndex, position;

    content = get(this, 'content');
    childsCurrentContentIndex = get(childView, 'contentIndex');

    options = options || {};
    this._prepareChildForReuse(childView);

    if (childsCurrentContentIndex !== contentIndex || options.force) {
      position = this.positionForIndex(contentIndex);

      set(childView, 'position', position);
      set(childView, 'contentIndex', contentIndex);
      set(childView, 'context', content.objectAt(contentIndex));
    }
  },

  positionForIndex: function(index){
    var elementWidth, width, columnCount, rowHeight, y, x;

    elementWidth = get(this, 'elementWidth') || 1;
    width = get(this, 'width') || 1;
    columnCount = get(this, 'columnCount');
    rowHeight = get(this, 'rowHeight');

    y = (rowHeight * floor(index/columnCount));
    x = (index % columnCount) * elementWidth;

    return {
      y: y,
      x: x
    };
  },

  _childViewCount: function() {
    var contentLength, childViewCountForHeight;

    contentLength = get(this, 'content.length');
    childViewCountForHeight = this._numChildViewsForViewport();

    return min(contentLength, childViewCountForHeight);
  },

  columnCount: Ember.computed('width', 'elementWidth', function() {
    var elementWidth, width, count;

    elementWidth = get(this, 'elementWidth');
    width = get(this, 'width');

    if (elementWidth) {
      count = floor(width / elementWidth);
    } else {
      count = 1;
    }

    return count;
  }),

  columnCountDidChange: Ember.observer(function(){
    var ratio, currentScrollTop, proposedScrollTop, maxScrollTop,
        scrollTop, lastColumnCount, newColumnCount;

    lastColumnCount = this._lastColumnCount;

    currentScrollTop = get(this, 'scrollTop');
    newColumnCount = get(this, 'columnCount');
    maxScrollTop = get(this, 'maxScrollTop');

    this._lastColumnCount = newColumnCount;

    if (lastColumnCount) {
      ratio = (lastColumnCount / newColumnCount);
      proposedScrollTop = currentScrollTop * ratio;
      scrollTop = min(maxScrollTop, proposedScrollTop);
      set(this, 'scrollTop', scrollTop);
    }
  }, 'columnCount'),

  maxScrollTop: Ember.computed('height', 'totalHeight', function(){
    var totalHeight, viewportHeight;

    totalHeight = get(this, 'totalHeight'),
    viewportHeight = get(this, 'height');

    return totalHeight - viewportHeight;
  }),

  _numChildViewsForViewport: function() {
    var height, rowHeight, paddingCount, columnCount;

    height = get(this, 'height');
    rowHeight = get(this, 'rowHeight');
    paddingCount = get(this, 'paddingCount');
    columnCount = get(this, 'columnCount');

    return (ceil(height / rowHeight) * columnCount) + (paddingCount * columnCount);
  },

  _startingIndex: function() {
    var scrollTop, rowHeight, columnCount, calculatedStartingIndex,
        contentLength, largestStartingIndex;

    contentLength = get(this, 'content.length');
    scrollTop = get(this, 'scrollTop');
    rowHeight = get(this, 'rowHeight');
    columnCount = get(this, 'columnCount');

    calculatedStartingIndex = floor(scrollTop / rowHeight) * columnCount;

    largestStartingIndex = max(contentLength - 1, 0);

    return min(calculatedStartingIndex, largestStartingIndex);
  },

  contentWillChange: Ember.beforeObserver(function() {
    var content;

    content = get(this, 'content');

    if (content) {
      content.removeArrayObserver(this);
    }
  }, 'content'),

  contentDidChange: Ember.observer(function() {
    addContentArrayObserver.call(this);
    syncChildViews.call(this);
  }, 'content'),

  heightOrWidthDidChange: Ember.observer(syncChildViews, 'height', 'width'),

  _addItemView: function(contentIndex){
    var itemViewClass, childView;

    itemViewClass = get(this, 'itemViewClass');
    childView = itemViewClass.create();

    this._reuseChildForContentIndex(childView, contentIndex);

    this.pushObject(childView);
   },

  /**
   @private

   Intelligently manages the number of childviews.

   @method _syncChildViews
   **/
  _syncChildViews: function(){
    var itemViewClass, startingIndex, childViewCount,
        endingIndex, numberOfChildViews, numberOfChildViewsNeeded,
        childViews, count, delta, index, childViewsLength, contentIndex;

    this.childViewsWillSync();

    childViewCount = this._childViewCount();
    childViews = this.positionOrderedChildViews();

    startingIndex = this._startingIndex();
    endingIndex = startingIndex + childViewCount;

    numberOfChildViewsNeeded = childViewCount;
    numberOfChildViews = childViews.length;

    delta = numberOfChildViewsNeeded - numberOfChildViews;

    if (delta === 0) {
      // no change
    } else if (delta > 0) { // more views are needed
      contentIndex = this._lastEndingIndex;

      for (count = 0; count < delta; count++, contentIndex++) {
        this._addItemView(contentIndex);
      }

    } else { // less views are needed
      childViews.
        splice(numberOfChildViewsNeeded, numberOfChildViews).
        forEach(removeAndDestroy, this);
    }

    this.scrollTo(get(this, 'scrollTop'), { force: true });

    this._lastStartingIndex = startingIndex;
    this._lastEndingIndex   = this._lastEndingIndex + delta;

    this.childViewsDidSync();
  },

  listItemViews: Ember.computed('[]', function(){
    return this.filter(detectListItemViews);
  }),

  positionOrderedChildViews: function() {
    return get(this, 'listItemViews').sort(sortByContentIndex);
  },

  arrayWillChange: Ember.K,

  // TODO: refactor
  arrayDidChange: function(content, start, removedCount, addedCount) {
    var index, contentIndex;

    if (this.state === 'inDOM') {
      // ignore if all changes are out of the visible change
      if( start >= this._lastStartingIndex || start < this._lastEndingIndex) {
        index = 0;
        // ignore all changes not in the visible range
        this.positionOrderedChildViews().
          slice(start, start + addedCount).
          forEach(function(childView){
            contentIndex = this._lastStartingIndex + index;
            this._reuseChildForContentIndex(childView, contentIndex, { force: true });
            index++;
          }, this);
      }

      syncChildViews.call(this);
    }
  }
});

function createScrollingView(){
  return Ember.View.createWithMixins({
    attributeBindings: ['style'],
    classNames: ['ember-list-scrolling-view'],

    style: Ember.computed(function() {
      return "height: " + get(this, 'parentView.totalHeight') + "px";
    }).property('parentView.totalHeight')
  });
}

function notifyMutationListeners() {
  if (Ember.View.notifyMutationListeners) {
    Ember.run.once(Ember.View, 'notifyMutationListeners');
  }
}

Ember.ListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
  init: function(){
    this._super();
    // overwrite the view's domManager.prepend to prepend inside the wrapper div
    this.domManager.prepend = function(view, html) {
      view.$('.ember-list-container').prepend(html);
      notifyMutationListeners();
    };
  },

  render: function(buffer) {
    buffer.push('
');
    this._super(buffer);
    buffer.push('
');
  },

  childViewsWillSync: function(){
    var scrollingView;

    scrollingView = get(this, '_scrollingView');

    this.removeObject(scrollingView);
  },

  childViewsDidSync: function(){
    var scrollingView;

    scrollingView = get(this, '_scrollingView');

    if (!scrollingView) {
      scrollingView =  createScrollingView();
      this.set('_scrollingView', scrollingView);
    }

    this.pushObject(scrollingView);
  }
});

Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
  touchMove: function(e){
    e.preventDefault();

    console.log('Attempt touchmove');
    // call scroller library
  },

  mouseWheel: function(e){
    e.preventDefault();

    console.log('Attempt mouseWheel');
    // call scroller library
  }
});
<commit_msg>Only modify ListView's domManager<commit_after>require('list-view/list_item_view');

var get = Ember.get, set = Ember.set,
min = Math.min, max = Math.max, floor = Math.floor,
ceil = Math.ceil;

function addContentArrayObserver() {
  var content = get(this, 'content');
  if (content) {
    content.addArrayObserver(this);
  }
}

function removeAndDestroy(object){
  this.removeObject(object);
  object.destroy();
}

function syncChildViews(){
  Ember.run.once(this, '_syncChildViews');
}

function sortByContentIndex (viewOne, viewTwo){
  return get(viewOne, 'contentIndex') - get(viewTwo, 'contentIndex');
}

function detectListItemViews(childView) {
  return Ember.ListItemView.detectInstance(childView);
}

var domManager = Ember.create(Ember.ContainerView.proto().domManager);

domManager.prepend = function(view, html) {
  view.$('.ember-list-container').prepend(html);
  notifyMutationListeners();
};

Ember.ListViewMixin = Ember.Mixin.create({
  itemViewClass: Ember.ListItemView,
  classNames: ['ember-list-view'],
  attributeBindings: ['style'],
  domManager: domManager,
  scrollTop: 0,
  _lastEndingIndex: 0,
  paddingCount: 1, // One row for padding

  init: function() {
    this._super();
    addContentArrayObserver.call(this);
    this._syncChildViews();
    this.columnCountDidChange();
  },

  style: Ember.computed('height', 'width', function() {
    var height, width, style;

    height = get(this, 'height');
    width = get(this, 'width');

    style = "height:" + height + "px;" + 'width:' + width  + 'px;';

    return style;
  }),

  didInsertElement: function() {
    var self, element;

    self = this,
    element = get(this, 'element');

    self._scroll = function(e) { self.scroll(e); };
    self._touchMove = function(e) { self.touchMove(e); };
    self._mouseWheel = function(e) { self.mouseWheel(e); };

    element.addEventListener('scroll',     this._scroll);
    element.addEventListener('touchmove',  this._touchMove);
    element.addEventListener('mousewheel', this._mouseWheel);
  },

  touchMove: Ember.K,
  mouseWheel: Ember.K,

  willDestroyElement: function() {
    var element;

    element = get(this, 'element');

    element.removeEventListener('scroll', this._scroll);
    element.removeEventListener('touchmove', this._touchMove);
    element.removeEventListener('mousewheel', this._mouseWheel);
  },

  // Browser fires the scroll event asynchronously
  scroll: function(e) {
    Ember.run(this, this.scrollTo, e.target.scrollTop);
  },

  scrollTo: function(scrollTop, options) {
    var contentLength, childViews, childViewsLength,
        startingIndex, endingIndex, childView, attrs,
        contentIndex, visibleEndingIndex, maxContentIndex,
        contentIndexEnd;

    options = options || { };

    set(this, 'scrollTop', scrollTop);
    contentLength = get(this, 'content.length');
    maxContentIndex = max(contentLength - 1, 0);
    childViews = get(this, 'listItemViews');
    childViewsLength =  childViews.length;

    startingIndex = this._startingIndex();
    visibleEndingIndex = startingIndex + this._numChildViewsForViewport();

    endingIndex = min(maxContentIndex, visibleEndingIndex);

    if (!options.force && startingIndex === this._lastStartingIndex && endingIndex === this._lastEndingIndex) {
      return;
    }

    contentIndexEnd = min(visibleEndingIndex, startingIndex + childViewsLength);

    for (contentIndex = startingIndex; contentIndex < contentIndexEnd; contentIndex++) {
      childView = childViews[contentIndex % childViewsLength];
      this._reuseChildForContentIndex(childView, contentIndex, options);
    }

    this._lastStartingIndex = startingIndex;
    this._lastEndingIndex = endingIndex;
  },

  childViewsWillSync: Ember.K,
  childViewsDidSync: Ember.K,

  totalHeight: Ember.computed('content.length', 'rowHeight', 'columnCount', function() {
    var contentLength, rowHeight, columnCount;

    contentLength = get(this, 'content.length');
    rowHeight = get(this, 'rowHeight');
    columnCount = get(this, 'columnCount');

    return (ceil(contentLength / columnCount)) * rowHeight;
  }),

  _prepareChildForReuse: function(childView) {
    childView.prepareForReuse();
  },

  _reuseChildForContentIndex: function(childView, contentIndex, options) {
    var content, childsCurrentContentIndex, position;

    content = get(this, 'content');
    childsCurrentContentIndex = get(childView, 'contentIndex');

    options = options || {};
    this._prepareChildForReuse(childView);

    if (childsCurrentContentIndex !== contentIndex || options.force) {
      position = this.positionForIndex(contentIndex);

      set(childView, 'position', position);
      set(childView, 'contentIndex', contentIndex);
      set(childView, 'context', content.objectAt(contentIndex));
    }
  },

  positionForIndex: function(index){
    var elementWidth, width, columnCount, rowHeight, y, x;

    elementWidth = get(this, 'elementWidth') || 1;
    width = get(this, 'width') || 1;
    columnCount = get(this, 'columnCount');
    rowHeight = get(this, 'rowHeight');

    y = (rowHeight * floor(index/columnCount));
    x = (index % columnCount) * elementWidth;

    return {
      y: y,
      x: x
    };
  },

  _childViewCount: function() {
    var contentLength, childViewCountForHeight;

    contentLength = get(this, 'content.length');
    childViewCountForHeight = this._numChildViewsForViewport();

    return min(contentLength, childViewCountForHeight);
  },

  columnCount: Ember.computed('width', 'elementWidth', function() {
    var elementWidth, width, count;

    elementWidth = get(this, 'elementWidth');
    width = get(this, 'width');

    if (elementWidth) {
      count = floor(width / elementWidth);
    } else {
      count = 1;
    }

    return count;
  }),

  columnCountDidChange: Ember.observer(function(){
    var ratio, currentScrollTop, proposedScrollTop, maxScrollTop,
        scrollTop, lastColumnCount, newColumnCount;

    lastColumnCount = this._lastColumnCount;

    currentScrollTop = get(this, 'scrollTop');
    newColumnCount = get(this, 'columnCount');
    maxScrollTop = get(this, 'maxScrollTop');

    this._lastColumnCount = newColumnCount;

    if (lastColumnCount) {
      ratio = (lastColumnCount / newColumnCount);
      proposedScrollTop = currentScrollTop * ratio;
      scrollTop = min(maxScrollTop, proposedScrollTop);
      set(this, 'scrollTop', scrollTop);
    }
  }, 'columnCount'),

  maxScrollTop: Ember.computed('height', 'totalHeight', function(){
    var totalHeight, viewportHeight;

    totalHeight = get(this, 'totalHeight'),
    viewportHeight = get(this, 'height');

    return totalHeight - viewportHeight;
  }),

  _numChildViewsForViewport: function() {
    var height, rowHeight, paddingCount, columnCount;

    height = get(this, 'height');
    rowHeight = get(this, 'rowHeight');
    paddingCount = get(this, 'paddingCount');
    columnCount = get(this, 'columnCount');

    return (ceil(height / rowHeight) * columnCount) + (paddingCount * columnCount);
  },

  _startingIndex: function() {
    var scrollTop, rowHeight, columnCount, calculatedStartingIndex,
        contentLength, largestStartingIndex;

    contentLength = get(this, 'content.length');
    scrollTop = get(this, 'scrollTop');
    rowHeight = get(this, 'rowHeight');
    columnCount = get(this, 'columnCount');

    calculatedStartingIndex = floor(scrollTop / rowHeight) * columnCount;

    largestStartingIndex = max(contentLength - 1, 0);

    return min(calculatedStartingIndex, largestStartingIndex);
  },

  contentWillChange: Ember.beforeObserver(function() {
    var content;

    content = get(this, 'content');

    if (content) {
      content.removeArrayObserver(this);
    }
  }, 'content'),

  contentDidChange: Ember.observer(function() {
    addContentArrayObserver.call(this);
    syncChildViews.call(this);
  }, 'content'),

  heightOrWidthDidChange: Ember.observer(syncChildViews, 'height', 'width'),

  _addItemView: function(contentIndex){
    var itemViewClass, childView;

    itemViewClass = get(this, 'itemViewClass');
    childView = itemViewClass.create();

    this._reuseChildForContentIndex(childView, contentIndex);

    this.pushObject(childView);
   },

  /**
   @private

   Intelligently manages the number of childviews.

   @method _syncChildViews
   **/
  _syncChildViews: function(){
    var itemViewClass, startingIndex, childViewCount,
        endingIndex, numberOfChildViews, numberOfChildViewsNeeded,
        childViews, count, delta, index, childViewsLength, contentIndex;

    this.childViewsWillSync();

    childViewCount = this._childViewCount();
    childViews = this.positionOrderedChildViews();

    startingIndex = this._startingIndex();
    endingIndex = startingIndex + childViewCount;

    numberOfChildViewsNeeded = childViewCount;
    numberOfChildViews = childViews.length;

    delta = numberOfChildViewsNeeded - numberOfChildViews;

    if (delta === 0) {
      // no change
    } else if (delta > 0) { // more views are needed
      contentIndex = this._lastEndingIndex;

      for (count = 0; count < delta; count++, contentIndex++) {
        this._addItemView(contentIndex);
      }

    } else { // less views are needed
      childViews.
        splice(numberOfChildViewsNeeded, numberOfChildViews).
        forEach(removeAndDestroy, this);
    }

    this.scrollTo(get(this, 'scrollTop'), { force: true });

    this._lastStartingIndex = startingIndex;
    this._lastEndingIndex   = this._lastEndingIndex + delta;

    this.childViewsDidSync();
  },

  listItemViews: Ember.computed('[]', function(){
    return this.filter(detectListItemViews);
  }),

  positionOrderedChildViews: function() {
    return get(this, 'listItemViews').sort(sortByContentIndex);
  },

  arrayWillChange: Ember.K,

  // TODO: refactor
  arrayDidChange: function(content, start, removedCount, addedCount) {
    var index, contentIndex;

    if (this.state === 'inDOM') {
      // ignore if all changes are out of the visible change
      if( start >= this._lastStartingIndex || start < this._lastEndingIndex) {
        index = 0;
        // ignore all changes not in the visible range
        this.positionOrderedChildViews().
          slice(start, start + addedCount).
          forEach(function(childView){
            contentIndex = this._lastStartingIndex + index;
            this._reuseChildForContentIndex(childView, contentIndex, { force: true });
            index++;
          }, this);
      }

      syncChildViews.call(this);
    }
  }
});

function createScrollingView(){
  return Ember.View.createWithMixins({
    attributeBindings: ['style'],
    classNames: ['ember-list-scrolling-view'],

    style: Ember.computed(function() {
      return "height: " + get(this, 'parentView.totalHeight') + "px";
    }).property('parentView.totalHeight')
  });
}

function notifyMutationListeners() {
  if (Ember.View.notifyMutationListeners) {
    Ember.run.once(Ember.View, 'notifyMutationListeners');
  }
}

Ember.ListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
  render: function(buffer) {
    buffer.push('
');
    this._super(buffer);
    buffer.push('
');
  },

  childViewsWillSync: function(){
    var scrollingView;

    scrollingView = get(this, '_scrollingView');

    this.removeObject(scrollingView);
  },

  childViewsDidSync: function(){
    var scrollingView;

    scrollingView = get(this, '_scrollingView');

    if (!scrollingView) {
      scrollingView =  createScrollingView();
      this.set('_scrollingView', scrollingView);
    }

    this.pushObject(scrollingView);
  }
});

Ember.VirtualListView = Ember.ContainerView.extend(Ember.ListViewMixin, {
  touchMove: function(e){
    e.preventDefault();

    console.log('Attempt touchmove');
    // call scroller library
  },

  mouseWheel: function(e){
    e.preventDefault();

    console.log('Attempt mouseWheel');
    // call scroller library
  }
});
<|endoftext|>


<commit_before>// Load dependencies
var amanda = require('../../dist/latest.js');

// Error methods
var errorMethods = [
  'getProperties',
  'getMessages'
];

/**
 * Test #1
 */
exports['Test #1'] = function(test) {

  var a = 0;

  var schema = {
    type: 'object',
    properties: {
      users: {
        type: 'array',
      }
    }
  };

  [
    'Hello',
    123,
    true,
    {},
    function() {}
  ].forEach(function(input) {

    amanda.validate({
      users: input
    }, schema, function(error) {

      a += 1;

      delete error[0].message;

      test.deepEqual(error[0], {
        property: 'users',
        propertyValue: input,
        validator: 'type',
        validatorValue: 'array'
      });

      test.equal(error.length, 1);

      errorMethods.forEach(function(method) {
        test.ok(error[method]);
      });

    });

  });

  [
    undefined,
    {},
    { users: undefined },
    { users: [] }
  ].forEach(function(input) {

    amanda.validate(input, schema, function(error) {

      a += 1;

      test.equal(error, undefined)

    });

  });

  test.equal(a, 9);
  test.done();

};

/**
 * Test #2
 */
exports['Test #2'] = function(test) {

  var a = 0;

  var schema = {
    type: 'object',
    properties: {
      users: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            name: {
              type: 'string'
            },
            surname: {
              type: 'string'
            }
          }
        }
      }
    }
  };

  [
    {
      input: { users: [123] },
      property: 'users[0]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'object'
    },
    {
      input: { users: [{}, 123] },
      property: 'users[1]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'object'
    },
    {
      input: { users: [{}, {}, 123] },
      property: 'users[2]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'object'
    },
    {
      input: { users: [{ name: 123 }] },
      property: 'users[0].name',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: { users: [{ name: 123 }, {}] },
      property: 'users[0].name',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: { users: [{}, { name: 123 }] },
      property: 'users[1].name',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: { users: [{}, { surname: 123 }] },
      property: 'users[1].surname',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: { users: [{}, {}, { name: 123 }] },
      property: 'users[2].name',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    }
  ].forEach(function(obj) {

    amanda.validate(obj.input, schema, function(error) {

      a += 1;

      delete error[0].message;

      test.deepEqual(error[0], {
        property: obj.property,
        propertyValue: obj.propertyValue,
        validator: obj.validator,
        validatorValue: obj.validatorValue
      });

      test.equal(error.length, 1);

      errorMethods.forEach(function(method) {
        test.ok(error[method]);
      });

    });

  });

  [
    undefined,
    {},
    { users: undefined },
    { users: [] },
    { users: [{}] },
    { users: [{}, {}, {}] },
    { users: [{ name: 'František' }] },
    { users: [{}, { name: 'František' }] },
    { users: [{ surname: 'Hába' }] },
    { users: [{}, { surname: 'Hába' }] },
    { users: [{ name: 'František', surname: 'Hába' }] },
    { users: [{}, { name: 'František', surname: 'Hába' }] },
    { users: [{}, {}, { name: 'František', surname: 'Hába' }] }
  ].forEach(function(input) {

    amanda.validate(input, schema, function(error) {

      a += 1;

      test.equal(error, undefined)

    });

  });

  test.equal(a, 21);
  test.done();

};

/**
 * Test #3
 */
exports['Test #3'] = function(test) {

  var a = 0;

  var schema = {
    type: 'object',
    properties: {
      users: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            languages: {
              type: 'array',
              items: {
                type: 'string'
              }
            }
          }
        }
      }
    }
  };

  [
    {
      input: { users: [{ languages: [123] }] },
      property: 'users[0].languages[0]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: { users: [{ languages: ['a', 'b', 123] }] },
      property: 'users[0].languages[2]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: { users: [{}, { languages: [123] }] },
      property: 'users[1].languages[0]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: { users: [{}, { languages: ['a', 'b', 123] }] },
      property: 'users[1].languages[2]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: { users: [{ languages: [123] }, {}] },
      property: 'users[0].languages[0]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    }
  ].forEach(function(obj) {

    amanda.validate(obj.input, schema, function(error) {

      a += 1;

      delete error[0].message;

      test.deepEqual(error[0], {
        property: obj.property,
        propertyValue: obj.propertyValue,
        validator: obj.validator,
        validatorValue: obj.validatorValue
      });

      test.equal(error.length, 1);

      errorMethods.forEach(function(method) {
        test.ok(error[method]);
      });

    });

  });

  [
    { users: [{}] },
    { users: [{ languages: [] }] },
    { users: [{ languages: ['a', 'b', 'c'] }] },
  ].forEach(function(input) {

    amanda.validate(input, schema, function(error) {

      a += 1;

      test.equal(error, undefined)

    });

  });

  test.equal(a, 8);
  test.done();

};

/**
 * Test #4
 */
exports['Test #4'] = function(test) {

  var a = 0;

  var schema = {
    type: 'array',
    items: {
      type: 'object',
      properties: {
        name: {
          type: 'string'
        },
        surname: {
          type: 'string'
        }
      }
    }
  };

  [
    {
      input: [123],
      property: '[0]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'object'
    },
    {
      input: [[]],
      property: '[0]',
      propertyValue: [],
      validator: 'type',
      validatorValue: 'object'
    },
    {
      input: [{ name: 123 }],
      property: '[0].name',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: [{ surname: 123 }],
      property: '[0].surname',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: [{}, { name: 123 }],
      property: '[1].name',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: [{}, { surname: 123 }],
      property: '[1].surname',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: [{}, { name: 123 }, {}],
      property: '[1].name',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    },
    {
      input: [{}, { surname: 123 }, {}],
      property: '[1].surname',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'string'
    }
  ].forEach(function(obj) {

    amanda.validate(obj.input, schema, function(error) {

      a += 1;

      delete error[0].message;

      test.deepEqual(error[0], {
        property: obj.property,
        propertyValue: obj.propertyValue,
        validator: obj.validator,
        validatorValue: obj.validatorValue
      });

      test.equal(error.length, 1);

      errorMethods.forEach(function(method) {
        test.ok(error[method]);
      });

    });

  });

  [
    undefined,
    [],
    [{}],
    [{}, {}]
  ].forEach(function(input) {

    amanda.validate(input, schema, function(error) {

      a += 1;

      test.equal(error, undefined)

    });

  });

  test.equal(a, 12);
  test.done();

};

/**
 * Test #5
 */
exports['Test #5'] = function(test) {

  var a = 0;

  var schema = {
    type: 'object',
    properties: {
      users: {
        type: 'array',
        items: {
          type: 'object'
        }
      },
      privacy: {
        type: 'object',
        properties: {
          public: {
            type: 'boolean'
          },
          private: {
            type: 'boolean'
          }
        }
      }
    }
  };

  [
    {
      input: { users: 123 },
      property: 'users',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'array'
    },
    {
      input: { privacy: 123 },
      property: 'privacy',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'object'
    },
    {
      input: { privacy: { public: 123 } },
      property: 'privacy.public',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'boolean'
    },
    {
      input: { privacy: { private: 123 } },
      property: 'privacy.private',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'boolean'
    },
    {
      input: { users: [123] },
      property: 'users[0]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'object'
    },
    {
      input: { users: [{}, 123] },
      property: 'users[1]',
      propertyValue: 123,
      validator: 'type',
      validatorValue: 'object'
    }
  ].forEach(function(obj) {

    amanda.validate(obj.input, schema, function(error) {

      a += 1;

      delete error[0].message;

      test.deepEqual(error[0], {
        property: obj.property,
        propertyValue: obj.propertyValue,
        validator: obj.validator,
        validatorValue: obj.validatorValue
      });

      test.equal(error.length, 1);

      errorMethods.forEach(function(method) {
        test.ok(error[method]);
      });

    });

  });

  [
    undefined,
    {},
    { users: [] },
    { privacy: {} },
    { users: [], privacy: {} },
    { users: [{}], privacy: {} },
    { users: [{}], privacy: { public: true } },
    { users: [{}], privacy: { private: true } },
    { users: [{}], privacy: { public: true, private: true } }
  ].forEach(function(input) {

    amanda.validate(input, schema, function(error) {

      a += 1;

      test.equal(error, undefined)

    });

  });

  test.equal(a, 15);
  test.done();

};
<commit_msg>Remove the obsolete ‘MixedSingleError’ test suite<commit_after><|endoftext|>


<commit_before>import {
  SYNC_RENDER,
  NO_RENDER,
  FORCE_RENDER,
  ASYNC_RENDER,
  ATTR_KEY
} from '../constants'
import options from '../options'
import { extend } from '../util'
import { enqueueRender } from '../render-queue'
import { getNodeProps } from './index'
import {
  diff,
  mounts,
  diffLevel,
  flushMounts,
  recollectNodeTree,
  removeChildren
} from './diff'
import { createComponent, collectComponent } from './component-recycler'
import { removeNode } from '../dom/index'
import { addScopedAttr, addScopedAttrStatic, getCtorName } from '../style'
import { proxyUpdate } from '../observe'

/** Set a component's `props` (generally derived from JSX attributes).
 *	@param {Object} props
 *	@param {Object} [opts]
 *	@param {boolean} [opts.renderSync=false]	If `true` and {@link options.syncComponentUpdates} is `true`, triggers synchronous rendering.
 *	@param {boolean} [opts.render=true]			If `false`, no render will be triggered.
 */
export function setComponentProps(component, props, opts, context, mountAll) {
  if (component._disable) return
  component._disable = true

  if ((component.__ref = props.ref)) delete props.ref
  if ((component.__key = props.key)) delete props.key

  if (!component.base || mountAll) {
    if (component.componentWillMount) component.componentWillMount()
    if (component.beforeInstall) component.beforeInstall()
    if (component.install) component.install()
    if (component.constructor.observe) {
      proxyUpdate(component)
    }
  } else if (component.receiveProps) {
    component.receiveProps(props, component.data, component.props)
  } else if (component.componentWillReceiveProps) {
    component.componentWillReceiveProps(props, context)
  }

  if (context && context !== component.context) {
    if (!component.prevContext) component.prevContext = component.context
    component.context = context
  }

  if (!component.prevProps) component.prevProps = component.props
  component.props = props

  component._disable = false

  if (opts !== NO_RENDER) {
    if (
      opts === SYNC_RENDER ||
      options.syncComponentUpdates !== false ||
      !component.base
    ) {
      renderComponent(component, SYNC_RENDER, mountAll)
    } else {
      enqueueRender(component)
    }
  }

  if (component.__ref) component.__ref(component)
}

/** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account.
 *	@param {Component} component
 *	@param {Object} [opts]
 *	@param {boolean} [opts.build=false]		If `true`, component will build and store a DOM node if not already associated with one.
 *	@private
 */
export function renderComponent(component, opts, mountAll, isChild) {
  if (component._disable) return

  let props = component.props,
    data = component.data,
    context = component.context,
    previousProps = component.prevProps || props,
    previousState = component.prevState || data,
    previousContext = component.prevContext || context,
    isUpdate = component.base,
    nextBase = component.nextBase,
    initialBase = isUpdate || nextBase,
    initialChildComponent = component._component,
    skip = false,
    rendered,
    inst,
    cbase

  // if updating
  if (isUpdate) {
    component.props = previousProps
    component.data = previousState
    component.context = previousContext
    if (
      opts !== FORCE_RENDER &&
      component.shouldComponentUpdate &&
      component.shouldComponentUpdate(props, data, context) === false
    ) {
      skip = true
    } else if (component.componentWillUpdate) {
      component.componentWillUpdate(props, data, context)
    } else if (component.beforeUpdate) {
      component.beforeUpdate(props, data, context)
    }
    component.props = props
    component.data = data
    component.context = context
  }

  component.prevProps = component.prevState = component.prevContext = component.nextBase = null

  if (!skip) {
    rendered = component.render(props, data, context)

    //don't rerender
    if (component.staticCss) {
      addScopedAttrStatic(
        rendered,
        component.staticCss(),
        '_style_' + getCtorName(component.constructor)
      )
    }

    if (component.css) {
      addScopedAttr(
        rendered,
        component.css(),
        '_style_' + component.elementId,
        component
      )
    }

    // context to pass to the child, can be updated via (grand-)parent component
    if (component.getChildContext) {
      context = extend(extend({}, context), component.getChildContext())
    }

    let childComponent = rendered && rendered.nodeName,
      toUnmount,
      base,
      ctor = options.mapping[childComponent]

    if (ctor) {
      // set up high order component link

      let childProps = getNodeProps(rendered)
      inst = initialChildComponent

      if (inst && inst.constructor === ctor && childProps.key == inst.__key) {
        setComponentProps(inst, childProps, SYNC_RENDER, context, false)
      } else {
        toUnmount = inst

        component._component = inst = createComponent(ctor, childProps, context)
        inst.nextBase = inst.nextBase || nextBase
        inst._parentComponent = component
        setComponentProps(inst, childProps, NO_RENDER, context, false)
        renderComponent(inst, SYNC_RENDER, mountAll, true)
      }

      base = inst.base
    } else {
      cbase = initialBase

      // destroy high order component link
      toUnmount = initialChildComponent
      if (toUnmount) {
        cbase = component._component = null
      }

      if (initialBase || opts === SYNC_RENDER) {
        if (cbase) cbase._component = null
        base = diff(
          cbase,
          rendered,
          context,
          mountAll || !isUpdate,
          initialBase && initialBase.parentNode,
          true
        )
      }
    }

    if (initialBase && base !== initialBase && inst !== initialChildComponent) {
      let baseParent = initialBase.parentNode
      if (baseParent && base !== baseParent) {
        baseParent.replaceChild(base, initialBase)

        if (!toUnmount) {
          initialBase._component = null
          recollectNodeTree(initialBase, false)
        }
      }
    }

    if (toUnmount) {
      unmountComponent(toUnmount)
    }

    component.base = base
    if (base && !isChild) {
      let componentRef = component,
        t = component
      while ((t = t._parentComponent)) {
        ;(componentRef = t).base = base
      }
      base._component = componentRef
      base._componentConstructor = componentRef.constructor
    }
  }

  if (!isUpdate || mountAll) {
    mounts.unshift(component)
  } else if (!skip) {
    // Ensure that pending componentDidMount() hooks of child components
    // are called before the componentDidUpdate() hook in the parent.
    // Note: disabled as it causes duplicate hooks, see https://github.com/developit/preact/issues/750
    // flushMounts();

    if (component.componentDidUpdate) {
      component.componentDidUpdate(
        previousProps,
        previousState,
        previousContext
      )
    }
    if (component.afterUpdate) {
      //deprecated
      component.afterUpdate(previousProps, previousState, previousContext)
    }
    if (component.updated) {
      component.updated(previousProps, previousState, previousContext)
    }
    if (options.afterUpdate) options.afterUpdate(component)
  }

  if (component._renderCallbacks != null) {
    while (component._renderCallbacks.length)
      component._renderCallbacks.pop().call(component)
  }

  if (!diffLevel && !isChild) flushMounts()
}

/** Apply the Component referenced by a VNode to the DOM.
 *	@param {Element} dom	The DOM node to mutate
 *	@param {VNode} vnode	A Component-referencing VNode
 *	@returns {Element} dom	The created/mutated element
 *	@private
 */
export function buildComponentFromVNode(dom, vnode, context, mountAll) {
  let c = dom && dom._component,
    originalComponent = c,
    oldDom = dom,
    isDirectOwner = c && dom._componentConstructor === vnode.nodeName,
    isOwner = isDirectOwner,
    props = getNodeProps(vnode)
  while (c && !isOwner && (c = c._parentComponent)) {
    isOwner = c.constructor === vnode.nodeName
  }

  if (c && isOwner && (!mountAll || c._component)) {
    setComponentProps(c, props, ASYNC_RENDER, context, mountAll)
    dom = c.base
  } else {
    if (originalComponent && !isDirectOwner) {
      unmountComponent(originalComponent)
      dom = oldDom = null
    }

    c = createComponent(vnode.nodeName, props, context)
    if (dom && !c.nextBase) {
      c.nextBase = dom
      // passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L229:
      oldDom = null
    }
    setComponentProps(c, props, SYNC_RENDER, context, mountAll)
    dom = c.base

    if (oldDom && dom !== oldDom) {
      oldDom._component = null
      recollectNodeTree(oldDom, false)
    }
  }

  return dom
}

/** Remove a component from the DOM and recycle it.
 *	@param {Component} component	The Component instance to unmount
 *	@private
 */
export function unmountComponent(component) {
  if (options.beforeUnmount) options.beforeUnmount(component)

  let base = component.base

  component._disable = true

  if (component.componentWillUnmount) component.componentWillUnmount()
  if (component.uninstall) component.uninstall()

  component.base = null

  // recursively tear down & recollect high-order component children:
  let inner = component._component
  if (inner) {
    unmountComponent(inner)
  } else if (base) {
    if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null)

    component.nextBase = base

    removeNode(base)
    collectComponent(component)

    removeChildren(base)
  }

  if (component.__ref) component.__ref(null)
}
<commit_msg>omio - supports beforeRender<commit_after>import {
  SYNC_RENDER,
  NO_RENDER,
  FORCE_RENDER,
  ASYNC_RENDER,
  ATTR_KEY
} from '../constants'
import options from '../options'
import { extend } from '../util'
import { enqueueRender } from '../render-queue'
import { getNodeProps } from './index'
import {
  diff,
  mounts,
  diffLevel,
  flushMounts,
  recollectNodeTree,
  removeChildren
} from './diff'
import { createComponent, collectComponent } from './component-recycler'
import { removeNode } from '../dom/index'
import { addScopedAttr, addScopedAttrStatic, getCtorName } from '../style'
import { proxyUpdate } from '../observe'

/** Set a component's `props` (generally derived from JSX attributes).
 *	@param {Object} props
 *	@param {Object} [opts]
 *	@param {boolean} [opts.renderSync=false]	If `true` and {@link options.syncComponentUpdates} is `true`, triggers synchronous rendering.
 *	@param {boolean} [opts.render=true]			If `false`, no render will be triggered.
 */
export function setComponentProps(component, props, opts, context, mountAll) {
  if (component._disable) return
  component._disable = true

  if ((component.__ref = props.ref)) delete props.ref
  if ((component.__key = props.key)) delete props.key

  if (!component.base || mountAll) {
    if (component.componentWillMount) component.componentWillMount()
    if (component.beforeInstall) component.beforeInstall()
    if (component.install) component.install()
    if (component.constructor.observe) {
      proxyUpdate(component)
    }
  } else if (component.receiveProps) {
    component.receiveProps(props, component.data, component.props)
  } else if (component.componentWillReceiveProps) {
    component.componentWillReceiveProps(props, context)
  }

  if (context && context !== component.context) {
    if (!component.prevContext) component.prevContext = component.context
    component.context = context
  }

  if (!component.prevProps) component.prevProps = component.props
  component.props = props

  component._disable = false

  if (opts !== NO_RENDER) {
    if (
      opts === SYNC_RENDER ||
      options.syncComponentUpdates !== false ||
      !component.base
    ) {
      renderComponent(component, SYNC_RENDER, mountAll)
    } else {
      enqueueRender(component)
    }
  }

  if (component.__ref) component.__ref(component)
}

/** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account.
 *	@param {Component} component
 *	@param {Object} [opts]
 *	@param {boolean} [opts.build=false]		If `true`, component will build and store a DOM node if not already associated with one.
 *	@private
 */
export function renderComponent(component, opts, mountAll, isChild) {
  if (component._disable) return

  let props = component.props,
    data = component.data,
    context = component.context,
    previousProps = component.prevProps || props,
    previousState = component.prevState || data,
    previousContext = component.prevContext || context,
    isUpdate = component.base,
    nextBase = component.nextBase,
    initialBase = isUpdate || nextBase,
    initialChildComponent = component._component,
    skip = false,
    rendered,
    inst,
    cbase

  // if updating
  if (isUpdate) {
    component.props = previousProps
    component.data = previousState
    component.context = previousContext
    if (
      opts !== FORCE_RENDER &&
      component.shouldComponentUpdate &&
      component.shouldComponentUpdate(props, data, context) === false
    ) {
      skip = true
    } else if (component.componentWillUpdate) {
      component.componentWillUpdate(props, data, context)
    } else if (component.beforeUpdate) {
      component.beforeUpdate(props, data, context)
    }
    component.props = props
    component.data = data
    component.context = context
  }

  component.prevProps = component.prevState = component.prevContext = component.nextBase = null

  if (!skip) {
    component.beforeRender && component.beforeRender()
    rendered = component.render(props, data, context)

    //don't rerender
    if (component.staticCss) {
      addScopedAttrStatic(
        rendered,
        component.staticCss(),
        '_style_' + getCtorName(component.constructor)
      )
    }

    if (component.css) {
      addScopedAttr(
        rendered,
        component.css(),
        '_style_' + component.elementId,
        component
      )
    }

    // context to pass to the child, can be updated via (grand-)parent component
    if (component.getChildContext) {
      context = extend(extend({}, context), component.getChildContext())
    }

    let childComponent = rendered && rendered.nodeName,
      toUnmount,
      base,
      ctor = options.mapping[childComponent]

    if (ctor) {
      // set up high order component link

      let childProps = getNodeProps(rendered)
      inst = initialChildComponent

      if (inst && inst.constructor === ctor && childProps.key == inst.__key) {
        setComponentProps(inst, childProps, SYNC_RENDER, context, false)
      } else {
        toUnmount = inst

        component._component = inst = createComponent(ctor, childProps, context)
        inst.nextBase = inst.nextBase || nextBase
        inst._parentComponent = component
        setComponentProps(inst, childProps, NO_RENDER, context, false)
        renderComponent(inst, SYNC_RENDER, mountAll, true)
      }

      base = inst.base
    } else {
      cbase = initialBase

      // destroy high order component link
      toUnmount = initialChildComponent
      if (toUnmount) {
        cbase = component._component = null
      }

      if (initialBase || opts === SYNC_RENDER) {
        if (cbase) cbase._component = null
        base = diff(
          cbase,
          rendered,
          context,
          mountAll || !isUpdate,
          initialBase && initialBase.parentNode,
          true
        )
      }
    }

    if (initialBase && base !== initialBase && inst !== initialChildComponent) {
      let baseParent = initialBase.parentNode
      if (baseParent && base !== baseParent) {
        baseParent.replaceChild(base, initialBase)

        if (!toUnmount) {
          initialBase._component = null
          recollectNodeTree(initialBase, false)
        }
      }
    }

    if (toUnmount) {
      unmountComponent(toUnmount)
    }

    component.base = base
    if (base && !isChild) {
      let componentRef = component,
        t = component
      while ((t = t._parentComponent)) {
        ;(componentRef = t).base = base
      }
      base._component = componentRef
      base._componentConstructor = componentRef.constructor
    }
  }

  if (!isUpdate || mountAll) {
    mounts.unshift(component)
  } else if (!skip) {
    // Ensure that pending componentDidMount() hooks of child components
    // are called before the componentDidUpdate() hook in the parent.
    // Note: disabled as it causes duplicate hooks, see https://github.com/developit/preact/issues/750
    // flushMounts();

    if (component.componentDidUpdate) {
      component.componentDidUpdate(
        previousProps,
        previousState,
        previousContext
      )
    }
    if (component.afterUpdate) {
      //deprecated
      component.afterUpdate(previousProps, previousState, previousContext)
    }
    if (component.updated) {
      component.updated(previousProps, previousState, previousContext)
    }
    if (options.afterUpdate) options.afterUpdate(component)
  }

  if (component._renderCallbacks != null) {
    while (component._renderCallbacks.length)
      component._renderCallbacks.pop().call(component)
  }

  if (!diffLevel && !isChild) flushMounts()
}

/** Apply the Component referenced by a VNode to the DOM.
 *	@param {Element} dom	The DOM node to mutate
 *	@param {VNode} vnode	A Component-referencing VNode
 *	@returns {Element} dom	The created/mutated element
 *	@private
 */
export function buildComponentFromVNode(dom, vnode, context, mountAll) {
  let c = dom && dom._component,
    originalComponent = c,
    oldDom = dom,
    isDirectOwner = c && dom._componentConstructor === vnode.nodeName,
    isOwner = isDirectOwner,
    props = getNodeProps(vnode)
  while (c && !isOwner && (c = c._parentComponent)) {
    isOwner = c.constructor === vnode.nodeName
  }

  if (c && isOwner && (!mountAll || c._component)) {
    setComponentProps(c, props, ASYNC_RENDER, context, mountAll)
    dom = c.base
  } else {
    if (originalComponent && !isDirectOwner) {
      unmountComponent(originalComponent)
      dom = oldDom = null
    }

    c = createComponent(vnode.nodeName, props, context)
    if (dom && !c.nextBase) {
      c.nextBase = dom
      // passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L229:
      oldDom = null
    }
    setComponentProps(c, props, SYNC_RENDER, context, mountAll)
    dom = c.base

    if (oldDom && dom !== oldDom) {
      oldDom._component = null
      recollectNodeTree(oldDom, false)
    }
  }

  return dom
}

/** Remove a component from the DOM and recycle it.
 *	@param {Component} component	The Component instance to unmount
 *	@private
 */
export function unmountComponent(component) {
  if (options.beforeUnmount) options.beforeUnmount(component)

  let base = component.base

  component._disable = true

  if (component.componentWillUnmount) component.componentWillUnmount()
  if (component.uninstall) component.uninstall()

  component.base = null

  // recursively tear down & recollect high-order component children:
  let inner = component._component
  if (inner) {
    unmountComponent(inner)
  } else if (base) {
    if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null)

    component.nextBase = base

    removeNode(base)
    collectComponent(component)

    removeChildren(base)
  }

  if (component.__ref) component.__ref(null)
}
<|endoftext|>


<commit_before>module.exports = function (grunt) {
    grunt.initConfig({
        "pkg": grunt.file.readJSON("package.json"),
        "meta": {
            "paths": {
                "source": "Source",
                "dist": "Distribution"
            }
        },
        "tslint": {
            "options": {
                "configuration": grunt.file.readJSON("tslint.json")
            },
            "files": {
                "src": ["<%= meta.paths.source %>/<%= pkg.name %>.ts"]
            }
        },
        "typescript": {
            "base": {
                "src": "<%= meta.paths.source %>/<%= pkg.name %>.ts",
                "dest": "<%= meta.paths.source %>/<%= pkg.name %>.js"
            }
        },
        "clean": ["<%= meta.paths.dist %>"],
        "copy": {
            "default": {
                "files": [{
                    "src": "<%= meta.paths.source %>/<%= pkg.name %>.ts",
                    "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts"
                }, {
                    "src": "<%= meta.paths.source %>/References/*.ts",
                    "dest": "<%= meta.paths.dist %>/",
                    "expand": true,
                    "flatten": true
                }, {
                    "src": "README.md",
                    "dest": "<%= meta.paths.dist %>/"
                }, {
                    "src": "README.md",
                    "dest": "<%= meta.paths.source %>/"
                }, {
                    "src": "LICENSE.txt",
                    "dest": "<%= meta.paths.dist %>/"
                }, {
                    "src": "LICENSE.txt",
                    "dest": "<%= meta.paths.source %>/"
                }]
            }
        },
        "uglify": {
            "options": {
                "compress": true
            },
            "dist": {
                "files": {
                    "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.min.js": ["<%= meta.paths.source %>/<%= pkg.name %>.js"],
                }
            }
        },
        "preprocess": {
            "dist": {
                "src": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts",
                "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts"
            }
        },
        "mocha_phantomjs": {
            "all": ["Tests/*.html"]
        }
    });

    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks("grunt-contrib-copy");
    grunt.loadNpmTasks("grunt-contrib-uglify");
    grunt.loadNpmTasks("grunt-mocha-phantomjs");
    grunt.loadNpmTasks("grunt-preprocess");
    grunt.loadNpmTasks("grunt-tslint");
    grunt.loadNpmTasks("grunt-typescript");
    grunt.registerTask("default", [
        "tslint", "typescript", "clean", "copy", "uglify", "preprocess", "mocha_phantomjs"
    ]);
};<commit_msg>Added JS maps to grunt build<commit_after>module.exports = function (grunt) {
    grunt.initConfig({
        "pkg": grunt.file.readJSON("package.json"),
        "meta": {
            "paths": {
                "source": "Source",
                "dist": "Distribution"
            }
        },
        "tslint": {
            "options": {
                "configuration": grunt.file.readJSON("tslint.json")
            },
            "files": {
                "src": ["<%= meta.paths.source %>/<%= pkg.name %>.ts"]
            }
        },
        "typescript": {
            "base": {
                "src": "<%= meta.paths.source %>/<%= pkg.name %>.ts",
                "dest": "<%= meta.paths.source %>/<%= pkg.name %>.js"
            }
        },
        "clean": ["<%= meta.paths.dist %>"],
        "copy": {
            "default": {
                "files": [{
                    "src": "<%= meta.paths.source %>/<%= pkg.name %>.js",
                    "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.js"
                }, {
                    "src": "<%= meta.paths.source %>/<%= pkg.name %>.ts",
                    "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts"
                }, {
                    "src": "<%= meta.paths.source %>/References/*.ts",
                    "dest": "<%= meta.paths.dist %>/",
                    "expand": true,
                    "flatten": true
                }, {
                    "src": "README.md",
                    "dest": "<%= meta.paths.dist %>/"
                }, {
                    "src": "README.md",
                    "dest": "<%= meta.paths.source %>/"
                }, {
                    "src": "LICENSE.txt",
                    "dest": "<%= meta.paths.dist %>/"
                }, {
                    "src": "LICENSE.txt",
                    "dest": "<%= meta.paths.source %>/"
                }]
            }
        },
        "uglify": {
            "options": {
                "compress": true,
                "screwIE8": true,
                "sourceMap": true
            },
            "dist": {
                "files": {
                    "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.min.js": ["<%= meta.paths.source %>/<%= pkg.name %>.js"],
                }
            }
        },
        "preprocess": {
            "dist": {
                "src": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts",
                "dest": "<%= meta.paths.dist %>/<%= pkg.name %>-<%= pkg.version %>.ts"
            }
        },
        "mocha_phantomjs": {
            "all": ["Tests/*.html"]
        }
    });

    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks("grunt-contrib-copy");
    grunt.loadNpmTasks("grunt-contrib-uglify");
    grunt.loadNpmTasks("grunt-mocha-phantomjs");
    grunt.loadNpmTasks("grunt-preprocess");
    grunt.loadNpmTasks("grunt-tslint");
    grunt.loadNpmTasks("grunt-typescript");
    grunt.registerTask("default", [
        "tslint", "typescript", "clean", "copy", "uglify", "preprocess", "mocha_phantomjs"
    ]);
};<|endoftext|>


<commit_before>/* @flow */

import TextDisplay from "./text";
import JsonDisplay from "./json";
import JavaScriptDisplay from "./javascript";
import HTMLDisplay from "./html";
import MarkdownDisplay from "./markdown";
import LaTeXDisplay from "./latex";
import SVGDisplay from "./svg";
import { PNGDisplay, JPEGDisplay, GIFDisplay } from "./image";

type Transform = {
  MIMETYPE: string
};

type Transforms = { [string]: Transform };
type DisplayOrder = Array<string>;

export type TransformRegister = {
  transforms: Transforms,
  displayOrder: DisplayOrder
};

const tfs = [
  JsonDisplay,
  JavaScriptDisplay,
  HTMLDisplay,
  MarkdownDisplay,
  LaTeXDisplay,
  SVGDisplay,
  GIFDisplay,
  PNGDisplay,
  JPEGDisplay,
  TextDisplay
];

export const standardTransforms: Transforms = {};

tfs.forEach(transform => {
  standardTransforms[transform.MIMETYPE] = transform;
});

export const standardDisplayOrder: DisplayOrder = tfs.map(
  transform => transform.MIMETYPE
);

export function registerTransform(
  { transforms, displayOrder }: TransformRegister,
  transform: Transform
) {
  return {
    transforms: {
      ...transforms,
      [transform.MIMETYPE]: transform
    },
    displayOrder: [transform.MIMETYPE, ...displayOrder]
  };
}

/**
 * Choose the richest mimetype available based on the displayOrder and transforms
 * @param  {Map}   bundle - Map({mimetype1: data1, mimetype2: data2, ...})
 * @param  {Array} ordered list of mimetypes - ['text/html', 'text/plain']
 * @param  {Map}   mimetype -> React Component - Map({'text/plain': TextTransform})
 * @return {string}          Richest mimetype
 */

export function richestMimetype(
  bundle: Object,
  order: DisplayOrder = standardDisplayOrder,
  tf: Transforms = standardTransforms
): ?string {
  return (
    [...Object.keys(bundle)]
      // we can only use those we have a transform for
      .filter(mimetype => tf[mimetype] && order.includes(mimetype))
      // the richest is based on the order in displayOrder
      .sort(mimetype => order.indexOf(mimetype))[0]
  );
}
export const transforms = standardTransforms;
export const displayOrder = standardDisplayOrder;
<commit_msg>fix: correct the mimetype compare function<commit_after>/* @flow */

import TextDisplay from "./text";
import JsonDisplay from "./json";
import JavaScriptDisplay from "./javascript";
import HTMLDisplay from "./html";
import MarkdownDisplay from "./markdown";
import LaTeXDisplay from "./latex";
import SVGDisplay from "./svg";
import { PNGDisplay, JPEGDisplay, GIFDisplay } from "./image";

type Transform = {
  MIMETYPE: string
};

type Transforms = { [string]: Transform };
type DisplayOrder = Array<string>;

export type TransformRegister = {
  transforms: Transforms,
  displayOrder: DisplayOrder
};

const tfs = [
  JsonDisplay,
  JavaScriptDisplay,
  HTMLDisplay,
  MarkdownDisplay,
  LaTeXDisplay,
  SVGDisplay,
  GIFDisplay,
  PNGDisplay,
  JPEGDisplay,
  TextDisplay
];

export const standardTransforms: Transforms = {};

tfs.forEach(transform => {
  standardTransforms[transform.MIMETYPE] = transform;
});

export const standardDisplayOrder: DisplayOrder = tfs.map(
  transform => transform.MIMETYPE
);

export function registerTransform(
  { transforms, displayOrder }: TransformRegister,
  transform: Transform
) {
  return {
    transforms: {
      ...transforms,
      [transform.MIMETYPE]: transform
    },
    displayOrder: [transform.MIMETYPE, ...displayOrder]
  };
}

/**
 * Choose the richest mimetype available based on the displayOrder and transforms
 * @param  {Map}   bundle - Map({mimetype1: data1, mimetype2: data2, ...})
 * @param  {Array} ordered list of mimetypes - ['text/html', 'text/plain']
 * @param  {Map}   mimetype -> React Component - Map({'text/plain': TextTransform})
 * @return {string}          Richest mimetype
 */

export function richestMimetype(
  bundle: Object,
  order: DisplayOrder = standardDisplayOrder,
  tf: Transforms = standardTransforms
): ?string {
  return (
    [...Object.keys(bundle)]
      // we can only use those we have a transform for
      .filter(mimetype => tf[mimetype] && order.includes(mimetype))
      // the richest is based on the order in displayOrder
      .sort((a, b) => order.indexOf(a) - order.indexOf(b))[0]
  );
}
export const transforms = standardTransforms;
export const displayOrder = standardDisplayOrder;
<|endoftext|>


<commit_before>const _ = require("lodash");
const path = require("path");
const Provider = require("truffle-provider");
const TruffleError = require("truffle-error");
const Module = require("module");
const findUp = require("find-up");
const originalrequire = require("original-require");
const Configstore = require("configstore");
const { EventManager } = require("../truffle-events");

const DEFAULT_CONFIG_FILENAME = "truffle-config.js";
const BACKUP_CONFIG_FILENAME = "truffle.js"; // old config filename

class Config {
  constructor(truffle_directory, working_directory, network) {
    const default_tx_values = {
      gas: 6721975,
      gasPrice: 20000000000, // 20 gwei,
      from: null
    };

    const eventManagerOptions = {
      muteReporters: this.quiet,
      logger: this.logger,
      globalConfig: this
    };
    this.events = new EventManager(eventManagerOptions);

    // This is a list of multi-level keys with defaults
    // we need to _.merge. Using this list for safety
    // vs. just merging all objects.
    this._deepCopy = ["compilers"];

    this._values = {
      truffle_directory:
        truffle_directory || path.resolve(path.join(__dirname, "../")),
      working_directory: working_directory || process.cwd(),
      network,
      networks: {},
      verboseRpc: false,
      gas: null,
      gasPrice: null,
      from: null,
      confirmations: 0,
      timeoutBlocks: 0,
      production: false,
      skipDryRun: false,
      build: null,
      resolver: null,
      artifactor: null,
      ethpm: {
        ipfs_host: "ipfs.infura.io",
        ipfs_protocol: "https",
        registry: "0x8011df4830b4f696cd81393997e5371b93338878",
        install_provider_uri: "https://ropsten.infura.io/truffle"
      },
      compilers: {
        solc: {
          settings: {
            optimizer: {
              enabled: false,
              runs: 200
            }
          }
        },
        vyper: {}
      },
      logger: {
        log() {}
      }
    };

    const resolveDirectory = value =>
      path.resolve(this.working_directory, value);

    const props = {
      // These are already set.
      truffle_directory() {},
      working_directory() {},
      network() {},
      networks() {},
      verboseRpc() {},
      build() {},
      resolver() {},
      artifactor() {},
      ethpm() {},
      logger() {},
      compilers() {},

      build_directory: {
        default: () => path.join(this.working_directory, "build"),
        transform: resolveDirectory
      },
      contracts_directory: {
        default: () => path.join(this.working_directory, "contracts"),
        transform: resolveDirectory
      },
      contracts_build_directory: {
        default: () => path.join(this.build_directory, "contracts"),
        transform: resolveDirectory
      },
      migrations_directory: {
        default: () => path.join(this.working_directory, "migrations"),
        transform: resolveDirectory
      },
      migrations_file_extension_regexp() {
        return /^\.(js|es6?)$/;
      },
      test_directory: {
        default: () => path.join(this.working_directory, "test"),
        transform: resolveDirectory
      },
      test_file_extension_regexp() {
        return /.*\.(js|ts|es|es6|jsx|sol)$/;
      },
      example_project_directory: {
        default: () => path.join(this.truffle_directory, "example"),
        transform: resolveDirectory
      },
      network_id: {
        get() {
          try {
            return this.network_config.network_id;
          } catch (e) {
            return null;
          }
        },
        set() {
          throw new Error(
            "Do not set config.network_id. Instead, set config.networks and then config.networks[<network name>].network_id"
          );
        }
      },
      network_config: {
        get() {
          const network = this.network;

          if (network === null || network === undefined) {
            throw new Error(
              "Network not set. Cannot determine network to use."
            );
          }

          let conf = this.networks[network];

          if (conf === null || conf === undefined) {
            config = {};
          }

          conf = _.extend({}, default_tx_values, conf);

          return conf;
        },
        set() {
          throw new Error(
            "Don't set config.network_config. Instead, set config.networks with the desired values."
          );
        }
      },
      from: {
        get() {
          try {
            return this.network_config.from;
          } catch (e) {
            return default_tx_values.from;
          }
        },
        set() {
          throw new Error(
            "Don't set config.from directly. Instead, set config.networks and then config.networks[<network name>].from"
          );
        }
      },
      gas: {
        get() {
          try {
            return this.network_config.gas;
          } catch (e) {
            return default_tx_values.gas;
          }
        },
        set() {
          throw new Error(
            "Don't set config.gas directly. Instead, set config.networks and then config.networks[<network name>].gas"
          );
        }
      },
      gasPrice: {
        get() {
          try {
            return this.network_config.gasPrice;
          } catch (e) {
            return default_tx_values.gasPrice;
          }
        },
        set() {
          throw new Error(
            "Don't set config.gasPrice directly. Instead, set config.networks and then config.networks[<network name>].gasPrice"
          );
        }
      },
      provider: {
        get() {
          if (!this.network) {
            return null;
          }

          const options = this.network_config;
          options.verboseRpc = this.verboseRpc;

          return Provider.create(options);
        },
        set() {
          throw new Error(
            "Don't set config.provider directly. Instead, set config.networks and then set config.networks[<network name>].provider"
          );
        }
      },
      confirmations: {
        get() {
          try {
            return this.network_config.confirmations;
          } catch (e) {
            return 0;
          }
        },
        set() {
          throw new Error(
            "Don't set config.confirmations directly. Instead, set config.networks and then config.networks[<network name>].confirmations"
          );
        }
      },
      production: {
        get() {
          try {
            return this.network_config.production;
          } catch (e) {
            return false;
          }
        },
        set() {
          throw new Error(
            "Don't set config.production directly. Instead, set config.networks and then config.networks[<network name>].production"
          );
        }
      },
      timeoutBlocks: {
        get() {
          try {
            return this.network_config.timeoutBlocks;
          } catch (e) {
            return 0;
          }
        },
        set() {
          throw new Error(
            "Don't set config.timeoutBlocks directly. Instead, set config.networks and then config.networks[<network name>].timeoutBlocks"
          );
        }
      }
    };

    Object.keys(props).forEach(prop => {
      this.addProp(prop, props[prop]);
    });
  }

  addProp(propertyName, descriptor) {
    // possible property descriptors
    //
    // supports `default` and `transform` in addition to `get` and `set`
    //
    // default: specify function to retrieve default value (used by get)
    // transform: specify function to transform value when (used by set)
    Object.defineProperty(this, propertyName, {
      // retrieve config property value
      get:
        descriptor.get ||
        function() {
          // value is specified
          if (propertyName in this._values) {
            return this._values[propertyName];
          }

          // default getter is specified
          if (descriptor.default) {
            return descriptor.default();
          }

          // descriptor is a function
          return descriptor();
        },
      set:
        descriptor.set ||
        function(value) {
          this._values[propertyName] = descriptor.transform
            ? descriptor.transform(value)
            : value;
        },
      configurable: true,
      enumerable: true
    });
  }

  normalize(obj) {
    const clone = {};
    Object.keys(obj).forEach(key => {
      try {
        clone[key] = obj[key];
      } catch (e) {
        // Do nothing with values that throw.
      }
    });
    return clone;
  }

  with(obj) {
    const normalized = this.normalize(obj);
    const current = this.normalize(this);
    attachNewEventManager(this, options);

    return _.extend({}, current, normalized);
  }

  merge(options) {
    let clone = this.normalize(options);

    // Only set keys for values that don't throw.
    const propertyNames = Object.keys(options);

    propertyNames.forEach(key => {
      try {
        if (typeof clone[key] === "object" && this._deepCopy.includes(key)) {
          this[key] = _.merge(this[key], clone[key]);
        } else {
          this[key] = clone[key];
        }
      } catch (e) {
        // Do nothing.
      }
    });
    attachNewEventManager(this, options);

    return this;
  }
}

Config.default = () => new Config();

Config.search = (options = {}, filename) => {
  const searchOptions = {
    cwd: options.working_directory || options.workingDirectory
  };

  if (!filename) {
    const isWin = /^win/.test(process.platform);
    const defaultConfig = findUp.sync(DEFAULT_CONFIG_FILENAME, searchOptions);
    const backupConfig = findUp.sync(BACKUP_CONFIG_FILENAME, searchOptions);
    if (defaultConfig && backupConfig) {
      console.warn(
        `Warning: Both ${DEFAULT_CONFIG_FILENAME} and ${BACKUP_CONFIG_FILENAME} were found. Using ${DEFAULT_CONFIG_FILENAME}.`
      );
      return defaultConfig;
    } else if (backupConfig && !defaultConfig) {
      if (isWin)
        console.warn(
          `Warning: Please rename ${BACKUP_CONFIG_FILENAME} to ${DEFAULT_CONFIG_FILENAME} to ensure Windows compatibility.`
        );
      return backupConfig;
    } else {
      return defaultConfig;
    }
  }

  return findUp.sync(filename, searchOptions);
};

Config.detect = (options = {}, filename) => {
  const configFile = Config.search(options, filename);

  if (!configFile) {
    throw new TruffleError("Could not find suitable configuration file.");
  }

  return Config.load(configFile, options);
};

// When new options are passed in, a new eventManager needs to be
// attached as it might override some options (e.g. { quiet: true })
const attachNewEventManager = (config, newOptions) => {
  const currentEventManagerOptions = config.events.initializationOptions;
  const { quiet, logger, globalConfig, reporters } = newOptions;
  const optionsToMerge = {};

  if (typeof quiet !== "undefined") optionsToMerge.quiet = quiet;
  if (typeof logger !== "undefined") optionsToMerge.logger = logger;
  if (typeof globalConfig !== "undefined")
    optionsToMerge.globalConfig = globalConfig;
  if (typeof reporters !== "undefined") optionsToMerge.reporters = reporters;

  const newEventManagerOptions = Object.assign(
    {},
    currentEventManagerOptions,
    optionsToMerge
  );

  config.events = new EventManager(newEventManagerOptions);
};

Config.load = (file, options) => {
  const config = new Config();

  config.working_directory = path.dirname(path.resolve(file));

  // The require-nocache module used to do this for us, but
  // it doesn't bundle very well. So we've pulled it out ourselves.
  delete require.cache[Module._resolveFilename(file, module)];
  const static_config = originalrequire(file);

  attachNewEventManager(config, static_config);
  config.merge(static_config);
  config.merge(options);

  return config;
};

Config.getUserConfig = () =>
  new Configstore("truffle", {}, { globalConfigPath: true });

Config.getTruffleDataDirectory = () => {
  const configStore = new Configstore(
    "truffle",
    {},
    { globalConfigPath: true }
  );
  return path.dirname(configStore.path);
};

module.exports = Config;
<commit_msg>Change variable name in truffle-config<commit_after>const _ = require("lodash");
const path = require("path");
const Provider = require("truffle-provider");
const TruffleError = require("truffle-error");
const Module = require("module");
const findUp = require("find-up");
const originalrequire = require("original-require");
const Configstore = require("configstore");
const { EventManager } = require("../truffle-events");

const DEFAULT_CONFIG_FILENAME = "truffle-config.js";
const BACKUP_CONFIG_FILENAME = "truffle.js"; // old config filename

class Config {
  constructor(truffle_directory, working_directory, network) {
    const default_tx_values = {
      gas: 6721975,
      gasPrice: 20000000000, // 20 gwei,
      from: null
    };

    const eventManagerOptions = {
      muteReporters: this.quiet,
      logger: this.logger,
      globalConfig: this
    };
    this.events = new EventManager(eventManagerOptions);

    // This is a list of multi-level keys with defaults
    // we need to _.merge. Using this list for safety
    // vs. just merging all objects.
    this._deepCopy = ["compilers"];

    this._values = {
      truffle_directory:
        truffle_directory || path.resolve(path.join(__dirname, "../")),
      working_directory: working_directory || process.cwd(),
      network,
      networks: {},
      verboseRpc: false,
      gas: null,
      gasPrice: null,
      from: null,
      confirmations: 0,
      timeoutBlocks: 0,
      production: false,
      skipDryRun: false,
      build: null,
      resolver: null,
      artifactor: null,
      ethpm: {
        ipfs_host: "ipfs.infura.io",
        ipfs_protocol: "https",
        registry: "0x8011df4830b4f696cd81393997e5371b93338878",
        install_provider_uri: "https://ropsten.infura.io/truffle"
      },
      compilers: {
        solc: {
          settings: {
            optimizer: {
              enabled: false,
              runs: 200
            }
          }
        },
        vyper: {}
      },
      logger: {
        log() {}
      }
    };

    const resolveDirectory = value =>
      path.resolve(this.working_directory, value);

    const props = {
      // These are already set.
      truffle_directory() {},
      working_directory() {},
      network() {},
      networks() {},
      verboseRpc() {},
      build() {},
      resolver() {},
      artifactor() {},
      ethpm() {},
      logger() {},
      compilers() {},

      build_directory: {
        default: () => path.join(this.working_directory, "build"),
        transform: resolveDirectory
      },
      contracts_directory: {
        default: () => path.join(this.working_directory, "contracts"),
        transform: resolveDirectory
      },
      contracts_build_directory: {
        default: () => path.join(this.build_directory, "contracts"),
        transform: resolveDirectory
      },
      migrations_directory: {
        default: () => path.join(this.working_directory, "migrations"),
        transform: resolveDirectory
      },
      migrations_file_extension_regexp() {
        return /^\.(js|es6?)$/;
      },
      test_directory: {
        default: () => path.join(this.working_directory, "test"),
        transform: resolveDirectory
      },
      test_file_extension_regexp() {
        return /.*\.(js|ts|es|es6|jsx|sol)$/;
      },
      example_project_directory: {
        default: () => path.join(this.truffle_directory, "example"),
        transform: resolveDirectory
      },
      network_id: {
        get() {
          try {
            return this.network_config.network_id;
          } catch (e) {
            return null;
          }
        },
        set() {
          throw new Error(
            "Do not set config.network_id. Instead, set config.networks and then config.networks[<network name>].network_id"
          );
        }
      },
      network_config: {
        get() {
          const network = this.network;

          if (network === null || network === undefined) {
            throw new Error(
              "Network not set. Cannot determine network to use."
            );
          }

          let conf = this.networks[network];

          if (conf === null || conf === undefined) {
            config = {};
          }

          conf = _.extend({}, default_tx_values, conf);

          return conf;
        },
        set() {
          throw new Error(
            "Don't set config.network_config. Instead, set config.networks with the desired values."
          );
        }
      },
      from: {
        get() {
          try {
            return this.network_config.from;
          } catch (e) {
            return default_tx_values.from;
          }
        },
        set() {
          throw new Error(
            "Don't set config.from directly. Instead, set config.networks and then config.networks[<network name>].from"
          );
        }
      },
      gas: {
        get() {
          try {
            return this.network_config.gas;
          } catch (e) {
            return default_tx_values.gas;
          }
        },
        set() {
          throw new Error(
            "Don't set config.gas directly. Instead, set config.networks and then config.networks[<network name>].gas"
          );
        }
      },
      gasPrice: {
        get() {
          try {
            return this.network_config.gasPrice;
          } catch (e) {
            return default_tx_values.gasPrice;
          }
        },
        set() {
          throw new Error(
            "Don't set config.gasPrice directly. Instead, set config.networks and then config.networks[<network name>].gasPrice"
          );
        }
      },
      provider: {
        get() {
          if (!this.network) {
            return null;
          }

          const options = this.network_config;
          options.verboseRpc = this.verboseRpc;

          return Provider.create(options);
        },
        set() {
          throw new Error(
            "Don't set config.provider directly. Instead, set config.networks and then set config.networks[<network name>].provider"
          );
        }
      },
      confirmations: {
        get() {
          try {
            return this.network_config.confirmations;
          } catch (e) {
            return 0;
          }
        },
        set() {
          throw new Error(
            "Don't set config.confirmations directly. Instead, set config.networks and then config.networks[<network name>].confirmations"
          );
        }
      },
      production: {
        get() {
          try {
            return this.network_config.production;
          } catch (e) {
            return false;
          }
        },
        set() {
          throw new Error(
            "Don't set config.production directly. Instead, set config.networks and then config.networks[<network name>].production"
          );
        }
      },
      timeoutBlocks: {
        get() {
          try {
            return this.network_config.timeoutBlocks;
          } catch (e) {
            return 0;
          }
        },
        set() {
          throw new Error(
            "Don't set config.timeoutBlocks directly. Instead, set config.networks and then config.networks[<network name>].timeoutBlocks"
          );
        }
      }
    };

    Object.keys(props).forEach(prop => {
      this.addProp(prop, props[prop]);
    });
  }

  addProp(propertyName, descriptor) {
    // possible property descriptors
    //
    // supports `default` and `transform` in addition to `get` and `set`
    //
    // default: specify function to retrieve default value (used by get)
    // transform: specify function to transform value when (used by set)
    Object.defineProperty(this, propertyName, {
      // retrieve config property value
      get:
        descriptor.get ||
        function() {
          // value is specified
          if (propertyName in this._values) {
            return this._values[propertyName];
          }

          // default getter is specified
          if (descriptor.default) {
            return descriptor.default();
          }

          // descriptor is a function
          return descriptor();
        },
      set:
        descriptor.set ||
        function(value) {
          this._values[propertyName] = descriptor.transform
            ? descriptor.transform(value)
            : value;
        },
      configurable: true,
      enumerable: true
    });
  }

  normalize(obj) {
    const clone = {};
    Object.keys(obj).forEach(key => {
      try {
        clone[key] = obj[key];
      } catch (e) {
        // Do nothing with values that throw.
      }
    });
    return clone;
  }

  with(options) {
    const normalized = this.normalize(options);
    const current = this.normalize(this);
    attachNewEventManager(this, options);

    return _.extend({}, current, normalized);
  }

  merge(options) {
    let clone = this.normalize(options);

    // Only set keys for values that don't throw.
    const propertyNames = Object.keys(options);

    propertyNames.forEach(key => {
      try {
        if (typeof clone[key] === "object" && this._deepCopy.includes(key)) {
          this[key] = _.merge(this[key], clone[key]);
        } else {
          this[key] = clone[key];
        }
      } catch (e) {
        // Do nothing.
      }
    });
    attachNewEventManager(this, options);

    return this;
  }
}

Config.default = () => new Config();

Config.search = (options = {}, filename) => {
  const searchOptions = {
    cwd: options.working_directory || options.workingDirectory
  };

  if (!filename) {
    const isWin = /^win/.test(process.platform);
    const defaultConfig = findUp.sync(DEFAULT_CONFIG_FILENAME, searchOptions);
    const backupConfig = findUp.sync(BACKUP_CONFIG_FILENAME, searchOptions);
    if (defaultConfig && backupConfig) {
      console.warn(
        `Warning: Both ${DEFAULT_CONFIG_FILENAME} and ${BACKUP_CONFIG_FILENAME} were found. Using ${DEFAULT_CONFIG_FILENAME}.`
      );
      return defaultConfig;
    } else if (backupConfig && !defaultConfig) {
      if (isWin)
        console.warn(
          `Warning: Please rename ${BACKUP_CONFIG_FILENAME} to ${DEFAULT_CONFIG_FILENAME} to ensure Windows compatibility.`
        );
      return backupConfig;
    } else {
      return defaultConfig;
    }
  }

  return findUp.sync(filename, searchOptions);
};

Config.detect = (options = {}, filename) => {
  const configFile = Config.search(options, filename);

  if (!configFile) {
    throw new TruffleError("Could not find suitable configuration file.");
  }

  return Config.load(configFile, options);
};

// When new options are passed in, a new eventManager needs to be
// attached as it might override some options (e.g. { quiet: true })
const attachNewEventManager = (config, newOptions) => {
  const currentEventManagerOptions = config.events.initializationOptions;
  const { quiet, logger, globalConfig, reporters } = newOptions;
  const optionsToMerge = {};

  if (typeof quiet !== "undefined") optionsToMerge.quiet = quiet;
  if (typeof logger !== "undefined") optionsToMerge.logger = logger;
  if (typeof globalConfig !== "undefined")
    optionsToMerge.globalConfig = globalConfig;
  if (typeof reporters !== "undefined") optionsToMerge.reporters = reporters;

  const newEventManagerOptions = Object.assign(
    {},
    currentEventManagerOptions,
    optionsToMerge
  );

  config.events = new EventManager(newEventManagerOptions);
};

Config.load = (file, options) => {
  const config = new Config();

  config.working_directory = path.dirname(path.resolve(file));

  // The require-nocache module used to do this for us, but
  // it doesn't bundle very well. So we've pulled it out ourselves.
  delete require.cache[Module._resolveFilename(file, module)];
  const static_config = originalrequire(file);

  attachNewEventManager(config, static_config);
  config.merge(static_config);
  config.merge(options);

  return config;
};

Config.getUserConfig = () =>
  new Configstore("truffle", {}, { globalConfigPath: true });

Config.getTruffleDataDirectory = () => {
  const configStore = new Configstore(
    "truffle",
    {},
    { globalConfigPath: true }
  );
  return path.dirname(configStore.path);
};

module.exports = Config;
<|endoftext|>