FIX: Upgrade Ember List View to add 1.9 support

This commit is contained in:
Robin Ward 2014-12-18 12:32:53 -05:00
parent 2d3a9b334d
commit b716682fe5
1 changed files with 166 additions and 143 deletions

View File

@ -118,10 +118,13 @@ var define, requireModule, require, requirejs;
})(); })();
define("list-view/helper", define("list-view/helper",
["exports"], ["./list_view","./virtual_list_view","exports"],
function(__exports__) { function(__dependency1__, __dependency2__, __exports__) {
"use strict"; "use strict";
__exports__["default"] = function emberList(options) { var EmberListView = __dependency1__["default"];
var EmberVirtualListView = __dependency2__["default"];
function createHelper (view, options) {
var hash = options.hash; var hash = options.hash;
var types = options.hashTypes; var types = options.hashTypes;
@ -132,8 +135,8 @@ define("list-view/helper",
delete types.items; delete types.items;
if (!hash.content) { if (!hash.content) {
hash.content = "this"; hash.content = 'this';
types.content = "ID"; types.content = 'ID';
} }
for (var prop in hash) { for (var prop in hash) {
@ -146,14 +149,27 @@ define("list-view/helper",
} }
} }
return Ember.Handlebars.helpers.collection.call(this, 'Ember.ListView', options); /*jshint validthis:true */
}; return Ember.Handlebars.helpers.collection.call(this, view, options);
}
function EmberList (options) {
return createHelper.call(this, EmberListView, options);
}
__exports__.EmberList = EmberList;__exports__["default"] = EmberList;
function EmberVirtualList (options) {
return createHelper.call(this, EmberVirtualListView, options);
}
__exports__.EmberVirtualList = EmberVirtualList;
}); });
define("list-view/list_item_view", define("list-view/list_item_view",
["list-view/list_item_view_mixin","exports"], ["list-view/list_item_view_mixin","exports"],
function(__dependency1__, __exports__) { function(__dependency1__, __exports__) {
"use strict"; "use strict";
// jshint validthis: true /*jshint validthis:true */
var ListItemViewMixin = __dependency1__["default"]; var ListItemViewMixin = __dependency1__["default"];
@ -186,7 +202,7 @@ define("list-view/list_item_view",
@namespace Ember @namespace Ember
*/ */
__exports__["default"] = Ember.View.extend(ListItemViewMixin, { __exports__["default"] = Ember.View.extend(ListItemViewMixin, {
updateContext: function(newContext){ updateContext: function(newContext) {
var context = get(this, 'context'); var context = get(this, 'context');
Ember.instrument('view.updateContext.render', this, function() { Ember.instrument('view.updateContext.render', this, function() {
@ -200,8 +216,7 @@ define("list-view/list_item_view",
}, },
rerender: function () { rerender: function () {
// todo: work around for tests. investigate a real fix. if (this.isDestroying || this.isDestroyed) {
if(get(this, 'isDestroying') || get(this, 'isDestroyed')) {
return; return;
} }
@ -217,9 +232,7 @@ define("list-view/list_item_view_mixin",
["exports"], ["exports"],
function(__exports__) { function(__exports__) {
"use strict"; "use strict";
// jshint validthis: true /*jshint validthis:true */
var get = Ember.get;
function samePosition(a, b) { function samePosition(a, b) {
return a && b && a.x === b.x && a.y === b.y; return a && b && a.x === b.x && a.y === b.y;
@ -229,7 +242,7 @@ define("list-view/list_item_view_mixin",
var element, position, _position; var element, position, _position;
Ember.instrument('view.updateContext.positionElement', this, function() { Ember.instrument('view.updateContext.positionElement', this, function() {
element = get(this, 'element'); element = this.element;
position = this.position; position = this.position;
_position = this._position; _position = this._position;
@ -248,21 +261,16 @@ define("list-view/list_item_view_mixin",
}, this); }, this);
} }
var TransformMixin = Ember.Mixin.create({ __exports__["default"] = Ember.Mixin.create({
classNames: ['ember-list-item-view'],
style: '', style: '',
attributeBindings: ['style'], attributeBindings: ['style'],
});
__exports__.TransformMixin = TransformMixin;
__exports__["default"] = Ember.Mixin.create(TransformMixin, {
_position: null, _position: null,
classNames: ['ember-list-item-view'],
_positionElement: positionElement, _positionElement: positionElement,
init: function(){ positionElementWhenInserted: Ember.on('init', function(){
this._super();
this.one('didInsertElement', positionElement); this.one('didInsertElement', positionElement);
}, }),
updatePosition: function(position) { updatePosition: function(position) {
this.position = position; this.position = position;
@ -277,7 +285,7 @@ define("list-view/list_view",
var ListViewHelper = __dependency1__["default"]; var ListViewHelper = __dependency1__["default"];
var ListViewMixin = __dependency2__["default"]; var ListViewMixin = __dependency2__["default"];
var get = Ember.get, set = Ember.set; var get = Ember.get;
/** /**
The `Ember.ListView` view class renders a The `Ember.ListView` view class renders a
@ -385,36 +393,30 @@ define("list-view/list_view",
applyTransform: ListViewHelper.applyTransform, applyTransform: ListViewHelper.applyTransform,
_scrollTo: function(scrollTop) { _scrollTo: function(scrollTop) {
var element = get(this, 'element'); var element = this.element;
if (element) { element.scrollTop = scrollTop; } if (element) { element.scrollTop = scrollTop; }
}, },
didInsertElement: function() { didInsertElement: function() {
var that = this; var that = this;
var element = get(this, 'element');
this._updateScrollableHeight(); this._updateScrollableHeight();
this._scroll = function(e) { that.scroll(e); }; this._scroll = function(e) { that.scroll(e); };
Ember.$(element).on('scroll', this._scroll); Ember.$(this.element).on('scroll', this._scroll);
}, },
willDestroyElement: function() { willDestroyElement: function() {
var element; Ember.$(this.element).off('scroll', this._scroll);
element = get(this, 'element');
Ember.$(element).off('scroll', this._scroll);
}, },
scroll: function(e) { scroll: function(e) {
this.scrollTo(e.target.scrollTop); this.scrollTo(e.target.scrollTop);
}, },
scrollTo: function(y){ scrollTo: function(y) {
var element = get(this, 'element');
this._scrollTo(y); this._scrollTo(y);
this._scrollContentTo(y); this._scrollContentTo(y);
}, },
@ -449,37 +451,27 @@ define("list-view/list_view_helper",
function(__exports__) { function(__exports__) {
"use strict"; "use strict";
// TODO - remove this! // TODO - remove this!
var el = document.createElement('div'), style = el.style; var el = document.body || document.createElement('div');
var set = Ember.set; var style = el.style;
var set = Ember.set;
function testProp (prop) { function getElementStyle (prop) {
var uppercaseProp = prop.charAt(0).toUpperCase() + prop.slice(1); var uppercaseProp = prop.charAt(0).toUpperCase() + prop.slice(1);
var dic = {
webkit: '-webkit-' + prop,
moz: '-moz-' + prop,
ms: 'ms' + uppercaseProp
};
var props = [ var props = [
prop, prop,
'webkit' + prop, 'webkit' + prop,
'webkit' + uppercaseProp, 'webkit' + uppercaseProp,
'Moz' + uppercaseProp, 'Moz' + uppercaseProp,
'moz' + uppercaseProp, 'moz' + uppercaseProp,
'ms' + uppercaseProp, 'ms' + uppercaseProp,
'ms' + prop 'ms' + prop
]; ];
for (var i=0; i < props.length; i++) { for (var i=0; i < props.length; i++) {
var property = props[i]; var property = props[i];
var prefix;
if (property in style) { if (property in style) {
prefix = property.toLowerCase().replace(prop, '');
if (prefix && dic[prefix]) {
return dic[prefix];
}
return property; return property;
} }
} }
@ -487,38 +479,67 @@ define("list-view/list_view_helper",
return null; return null;
} }
var transformProp = testProp('transform'); function getCSSStyle (attr) {
var perspectiveProp = testProp('perspective'); var styleName = getElementStyle(attr);
var supports2D = transformProp !== null; var prefix = styleName.toLowerCase().replace(attr, '');
var supports3D = perspectiveProp !== null;
var dic = {
webkit: '-webkit-' + attr,
moz: '-moz-' + attr,
ms: '-ms-' + attr
};
if (prefix && dic[prefix]) {
return dic[prefix];
}
return styleName;
}
var styleAttributeName = getElementStyle('transform');
var transformProp = getCSSStyle('transform');
var perspectiveProp = getElementStyle('perspective');
var supports2D = !!transformProp;
var supports3D = !!perspectiveProp;
function setStyle (optionalStyleString) {
return function (obj, x, y) {
var isElement = obj instanceof Element;
if (optionalStyleString && (supports2D || supports3D)) {
var style = Ember.String.fmt(optionalStyleString, x, y);
if (isElement) {
obj.style[styleAttributeName] = style;
} else {
set(obj, 'style', transformProp + ': ' + style);
}
} else {
if (isElement) {
obj.style.top = y;
obj.style.left = x;
}
}
};
}
__exports__["default"] = { __exports__["default"] = {
transformProp: transformProp, transformProp: transformProp,
applyTransform: (function(){ applyTransform: (function () {
if (supports2D) { if (supports2D) {
return function(childView, x, y){ return setStyle('translate(%@px, %@px)');
set(childView, 'style', transformProp + ': translate(' + x + 'px, ' + y + 'px);');
};
} else {
return function(childView, x, y){
set(childView, 'style', 'top: ' + y + 'px; left: ' + x + 'px;');
};
} }
return setStyle();
})(), })(),
apply3DTransform: (function(){ apply3DTransform: (function () {
if (supports3D) { if (supports3D) {
return function(childView, x, y){ return setStyle('translate3d(%@px, %@px, 0)');
set(childView, 'style', transformProp + ': translate3d(' + x + 'px, ' + y + 'px, 0);');
};
} else if (supports2D) { } else if (supports2D) {
return function(childView, x, y){ return setStyle('translate(%@px, %@px)');
set(childView, 'style', transformProp + ': translate(' + x + 'px, ' + y + 'px);');
};
} else {
return function(childView, x, y){
set(childView, 'style', 'top: ' + y + 'px; left: ' + x + 'px;');
};
} }
return setStyle();
})() })()
}; };
}); });
@ -526,15 +547,17 @@ define("list-view/list_view_mixin",
["list-view/reusable_list_item_view","exports"], ["list-view/reusable_list_item_view","exports"],
function(__dependency1__, __exports__) { function(__dependency1__, __exports__) {
"use strict"; "use strict";
// jshint validthis: true /*jshint validthis:true */
var ReusableListItemView = __dependency1__["default"]; var ReusableListItemView = __dependency1__["default"];
var get = Ember.get, set = Ember.set, var get = Ember.get;
var set = Ember.set;
min = Math.min, max = Math.max, floor = Math.floor, var min = Math.min;
ceil = Math.ceil, var max = Math.max;
forEach = Ember.ArrayPolyfills.forEach; var floor = Math.floor;
var ceil = Math.ceil;
var forEach = Ember.ArrayPolyfills.forEach;
function addContentArrayObserver() { function addContentArrayObserver() {
var content = get(this, 'content'); var content = get(this, 'content');
@ -556,12 +579,6 @@ define("list-view/list_view_mixin",
return get(viewOne, 'contentIndex') - get(viewTwo, 'contentIndex'); return get(viewOne, 'contentIndex') - get(viewTwo, 'contentIndex');
} }
function notifyMutationListeners() {
if (Ember.View.notifyMutationListeners) {
Ember.run.once(Ember.View, 'notifyMutationListeners');
}
}
function removeEmptyView() { function removeEmptyView() {
var emptyView = get(this, 'emptyView'); var emptyView = get(this, 'emptyView');
if (emptyView && emptyView instanceof Ember.View) { if (emptyView && emptyView instanceof Ember.View) {
@ -594,11 +611,11 @@ define("list-view/list_view_mixin",
} }
function enableProfilingOutput() { function enableProfilingOutput() {
function before(name, time, payload) { function before(name, time/*, payload*/) {
console.time(name); console.time(name);
} }
function after (name, time, payload) { function after (name, time/*, payload*/) {
console.timeEnd(name); console.timeEnd(name);
} }
@ -666,21 +683,26 @@ define("list-view/list_view_mixin",
@method render @method render
@param {Ember.RenderBuffer} buffer The render buffer @param {Ember.RenderBuffer} buffer The render buffer
*/ */
render: function(buffer) { render: function (buffer) {
buffer.push('<div class="ember-list-container"></div>'); var element = buffer.element();
this._super(buffer); var dom = buffer.dom;
var container = dom.createElement('div');
container.className = 'ember-list-container';
element.appendChild(container);
this._childViewsMorph = dom.createMorph(container, container, null);
return container;
}, },
createChildViewsMorph: function (element) { createChildViewsMorph: function (element) {
var children = element.children; this._childViewsMorph = this._renderer._dom.createMorph(element.lastChild, element.lastChild, null);
element = children[0];
this._childViewsMorph = this._renderer._dom.createMorph(element, children[children.length - 1], null);
return element; return element;
}, },
willInsertElement: function() { willInsertElement: function() {
if (!this.get("height") || !this.get("rowHeight")) { if (!this.get('height') || !this.get('rowHeight')) {
throw new Error("A ListView must be created with a height and a rowHeight."); throw new Error('A ListView must be created with a height and a rowHeight.');
} }
this._super(); this._super();
}, },
@ -857,6 +879,10 @@ define("list-view/list_view_mixin",
childView.prepareForReuse(); childView.prepareForReuse();
}, },
createChildView: function (_view) {
return this._super(_view, this._itemViewProps || {});
},
/** /**
@private @private
@method _reuseChildForContentIndex @method _reuseChildForContentIndex
@ -869,16 +895,14 @@ define("list-view/list_view_mixin",
if (childView.constructor !== contentViewClass) { if (childView.constructor !== contentViewClass) {
// rather then associative arrays, lets move childView + contentEntry maping to a Map // rather then associative arrays, lets move childView + contentEntry maping to a Map
var i = this._childViews.indexOf(childView); var i = this._childViews.indexOf(childView);
childView.destroy(); childView.destroy();
childView = this.createChildView(contentViewClass); childView = this.createChildView(contentViewClass);
this.insertAt(i, childView); this.insertAt(i, childView);
} }
content = get(this, 'content'); content = get(this, 'content');
enableProfiling = get(this, 'enableProfiling'); enableProfiling = get(this, 'enableProfiling');
position = this.positionForIndex(contentIndex); position = this.positionForIndex(contentIndex);
childView.updatePosition(position); childView.updatePosition(position);
set(childView, 'contentIndex', contentIndex); set(childView, 'contentIndex', contentIndex);
@ -1005,7 +1029,7 @@ define("list-view/list_view_mixin",
currentScrollTop = this.scrollTop; currentScrollTop = this.scrollTop;
newColumnCount = get(this, 'columnCount'); newColumnCount = get(this, 'columnCount');
maxScrollTop = get(this, 'maxScrollTop'); maxScrollTop = get(this, 'maxScrollTop');
element = get(this, 'element'); element = this.element;
this._lastColumnCount = newColumnCount; this._lastColumnCount = newColumnCount;
@ -1165,9 +1189,7 @@ define("list-view/list_view_mixin",
@event contentWillChange @event contentWillChange
*/ */
contentWillChange: Ember.beforeObserver(function() { contentWillChange: Ember.beforeObserver(function() {
var content; var content = get(this, 'content');
content = get(this, 'content');
if (content) { if (content) {
content.removeArrayObserver(this); content.removeArrayObserver(this);
@ -1198,12 +1220,11 @@ define("list-view/list_view_mixin",
@param {Number} contentIndex item index in the content array @param {Number} contentIndex item index in the content array
@method _addItemView @method _addItemView
*/ */
_addItemView: function(contentIndex){ _addItemView: function (contentIndex) {
var itemViewClass, childView; var itemViewClass, childView;
itemViewClass = this.itemViewForIndex(contentIndex); itemViewClass = this.itemViewForIndex(contentIndex);
childView = this.createChildView(itemViewClass); childView = this.createChildView(itemViewClass);
this.pushObject(childView); this.pushObject(childView);
}, },
@ -1242,13 +1263,13 @@ define("list-view/list_view_mixin",
@method _syncChildViews @method _syncChildViews
**/ **/
_syncChildViews: function(){ _syncChildViews: function () {
var childViews, childViewCount, var childViews, childViewCount,
numberOfChildViews, numberOfChildViewsNeeded, numberOfChildViews, numberOfChildViewsNeeded,
contentIndex, startingIndex, endingIndex, contentIndex, startingIndex, endingIndex,
contentLength, emptyView, count, delta; contentLength, emptyView, count, delta;
if (get(this, 'isDestroyed') || get(this, 'isDestroying')) { if (this.isDestroyed || this.isDestroying) {
return; return;
} }
@ -1328,18 +1349,18 @@ define("list-view/list_view_mixin",
contentIndex, visibleEndingIndex, maxContentIndex, contentIndex, visibleEndingIndex, maxContentIndex,
contentIndexEnd, scrollTop; contentIndexEnd, scrollTop;
scrollTop = this.scrollTop; scrollTop = this.scrollTop;
contentLength = get(this, 'content.length'); contentLength = get(this, 'content.length');
maxContentIndex = max(contentLength - 1, 0); maxContentIndex = max(contentLength - 1, 0);
childViews = this.getReusableChildViews(); childViews = this.getReusableChildViews();
childViewsLength = childViews.length; childViewsLength = childViews.length;
startingIndex = this._startingIndex(); startingIndex = this._startingIndex();
visibleEndingIndex = startingIndex + this._numChildViewsForViewport(); visibleEndingIndex = startingIndex + this._numChildViewsForViewport();
endingIndex = min(maxContentIndex, visibleEndingIndex); endingIndex = min(maxContentIndex, visibleEndingIndex);
contentIndexEnd = min(visibleEndingIndex, startingIndex + childViewsLength); contentIndexEnd = min(visibleEndingIndex, startingIndex + childViewsLength);
for (contentIndex = startingIndex; contentIndex < contentIndexEnd; contentIndex++) { for (contentIndex = startingIndex; contentIndex < contentIndexEnd; contentIndex++) {
childView = childViews[contentIndex % childViewsLength]; childView = childViews[contentIndex % childViewsLength];
@ -1374,7 +1395,7 @@ define("list-view/list_view_mixin",
var index, contentIndex, state; var index, contentIndex, state;
if (this._isChildEmptyView()) { if (this._isChildEmptyView()) {
removeEmptyView.call(this); removeEmptyView.call(this);
} }
// Support old and new Ember versions // Support old and new Ember versions
@ -1421,7 +1442,8 @@ define("list-view/main",
var ReusableListItemView = __dependency1__["default"]; var ReusableListItemView = __dependency1__["default"];
var VirtualListView = __dependency2__["default"]; var VirtualListView = __dependency2__["default"];
var ListItemView = __dependency3__["default"]; var ListItemView = __dependency3__["default"];
var EmberList = __dependency4__["default"]; var EmberList = __dependency4__.EmberList;
var EmberVirtualList = __dependency4__.EmberVirtualList;
var ListView = __dependency5__["default"]; var ListView = __dependency5__["default"];
var ListViewHelper = __dependency6__["default"]; var ListViewHelper = __dependency6__["default"];
@ -1432,6 +1454,7 @@ define("list-view/main",
Ember.ListViewHelper = ListViewHelper; Ember.ListViewHelper = ListViewHelper;
Ember.Handlebars.registerHelper('ember-list', EmberList); Ember.Handlebars.registerHelper('ember-list', EmberList);
Ember.Handlebars.registerHelper('ember-virtual-list', EmberVirtualList);
}); });
define("list-view/reusable_list_item_view", define("list-view/reusable_list_item_view",
["list-view/list_item_view_mixin","exports"], ["list-view/list_item_view_mixin","exports"],
@ -1442,20 +1465,24 @@ define("list-view/reusable_list_item_view",
var get = Ember.get, set = Ember.set; var get = Ember.get, set = Ember.set;
__exports__["default"] = Ember.View.extend(ListItemViewMixin, { __exports__["default"] = Ember.View.extend(ListItemViewMixin, {
init: function(){ prepareForReuse: Ember.K,
init: function () {
this._super(); this._super();
var context = Ember.ObjectProxy.create(); var context = Ember.ObjectProxy.create();
this.set('context', context); this.set('context', context);
this._proxyContext = context; this._proxyContext = context;
}, },
isVisible: Ember.computed('context.content', function(){
isVisible: Ember.computed('context.content', function () {
return !!this.get('context.content'); return !!this.get('context.content');
}), }),
updateContext: function(newContext){
var context = get(this._proxyContext, 'content'), state; updateContext: function (newContext) {
var context = get(this._proxyContext, 'content');
// Support old and new Ember versions // Support old and new Ember versions
state = this._state || this.state; var state = this._state || this.state;
if (context !== newContext) { if (context !== newContext) {
if (state === 'inDOM') { if (state === 'inDOM') {
@ -1468,15 +1495,15 @@ define("list-view/reusable_list_item_view",
set(this, 'controller', newContext); set(this, 'controller', newContext);
} }
} }
}, }
prepareForReuse: Ember.K
}); });
}); });
define("list-view/virtual_list_scroller_events", define("list-view/virtual_list_scroller_events",
["exports"], ["exports"],
function(__exports__) { function(__exports__) {
"use strict"; "use strict";
// jshint validthis: true /*jshint validthis:true */
var fieldRegex = /input|textarea|select/i, var fieldRegex = /input|textarea|select/i,
hasTouch = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch, hasTouch = ('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch,
handleStart, handleMove, handleEnd, handleCancel, handleStart, handleMove, handleEnd, handleCancel,
@ -1620,19 +1647,18 @@ define("list-view/virtual_list_scroller_events",
} }
}); });
define("list-view/virtual_list_view", define("list-view/virtual_list_view",
["list-view/list_view_mixin","list-view/list_item_view_mixin","list-view/list_view_helper","list-view/virtual_list_scroller_events","exports"], ["list-view/list_view_mixin","list-view/list_view_helper","list-view/virtual_list_scroller_events","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
"use strict"; "use strict";
/* /*
global Scroller global Scroller
*/ */
var ListViewMixin = __dependency1__["default"]; var ListViewMixin = __dependency1__["default"];
var TransformMixin = __dependency2__.TransformMixin; var ListViewHelper = __dependency2__["default"];
var ListViewHelper = __dependency3__["default"]; var VirtualListScrollerEvents = __dependency3__["default"];
var VirtualListScrollerEvents = __dependency4__["default"];
var max = Math.max, get = Ember.get, set = Ember.set; var get = Ember.get;
function updateScrollerDimensions(target) { function updateScrollerDimensions(target) {
var width, height, totalHeight; var width, height, totalHeight;
@ -1670,11 +1696,9 @@ define("list-view/virtual_list_view",
applyTransform: ListViewHelper.apply3DTransform, applyTransform: ListViewHelper.apply3DTransform,
setupScroller: function(){ setupScroller: function(){
var view, y; var view = this;
view = this; view.scroller = new Scroller(function(left, top/*, zoom*/) {
view.scroller = new Scroller(function(left, top, zoom) {
// Support old and new Ember versions // Support old and new Ember versions
var state = view._state || view.state; var state = view._state || view.state;
@ -1706,15 +1730,14 @@ define("list-view/virtual_list_view",
this._activateScrollerPullToRefresh(); this._activateScrollerPullToRefresh();
}, },
_insertPullToRefreshView: function(){ _insertPullToRefreshView: function(){
var pulldownClass = this.pullToRefreshViewClass.extend(TransformMixin); this.pullToRefreshView = this.createChildView(this.pullToRefreshViewClass);
this.pullToRefreshView = this.createChildView(pulldownClass);
this.insertAt(0, this.pullToRefreshView); this.insertAt(0, this.pullToRefreshView);
var view = this; var view = this;
this.pullToRefreshView.on('didInsertElement', function(){ this.pullToRefreshView.on('didInsertElement', function() {
Ember.run.schedule('afterRender', this, function(){ Ember.run.scheduleOnce('afterRender', this, function(){
view.applyTransform(this, 0, -1 * view.pullToRefreshViewHeight); view.applyTransform(this.element, 0, -1 * view.pullToRefreshViewHeight);
}); });
}); });
}, },