Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polyfill support for __proto__ in IE <= 10 #439

Merged
merged 4 commits into from
Jun 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions addon/classes/Column.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Ember from 'ember';
import fixProto from 'ember-light-table/utils/fix-proto';

const {
guidFor,
Expand Down Expand Up @@ -344,3 +345,6 @@ export default class Column extends EmberObject.extend({
this.set('subColumns', subColumns);
}
}

// https://github.com/offirgolan/ember-light-table/issues/436#issuecomment-310138868
fixProto(Column);
4 changes: 4 additions & 0 deletions addon/classes/Row.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Ember from 'ember';
import fixProto from 'ember-light-table/utils/fix-proto';

const {
computed,
Expand Down Expand Up @@ -100,3 +101,6 @@ export default class Row extends ObjectProxy.extend({
this.set('content', content);
}
}

// https://github.com/offirgolan/ember-light-table/issues/436#issuecomment-310138868
fixProto(Row);
4 changes: 4 additions & 0 deletions addon/classes/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Row from 'ember-light-table/classes/Row';
import Column from 'ember-light-table/classes/Column';
import SyncArrayProxy from 'ember-light-table/-private/sync-array-proxy';
import { mergeOptionsWithGlobals } from 'ember-light-table/-private/global-options';
import fixProto from 'ember-light-table/utils/fix-proto';

const {
get,
Expand Down Expand Up @@ -425,3 +426,6 @@ export default class Table extends EmberObject.extend({
return columns.map((c) => Table.createColumn(c));
}
}

// https://github.com/offirgolan/ember-light-table/issues/436#issuecomment-310138868
fixProto(Table);
76 changes: 76 additions & 0 deletions addon/utils/fix-proto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* @module Utils
* @private
*/

/**
* Internet Explorer <= 10 has no support for `__proto__`.
* This conditional polyfill works around that, so that static methods like
* `reopen` may be used. There are some caveats though.
*
* For more information, please see: [Issue #436 (comment)][436]
*
* [436]: https://github.com/offirgolan/ember-light-table/issues/436#issuecomment-310138868
*
* @class fixProto
*/

/**
* Whether or not this environment supports `__proto__`.
* IE <= 10 is known to not support it-
*
* More information on `__proto__`:
*
* https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
*
* @const isProtoSupported
* @type {Boolean}
* @readOnly
*/
export const isProtoSupported = ({ __proto__: [] }) instanceof Array;

/**
* Assigns all properties of `defaults` to `obj`, if they are not already
* defined on `obj`. This means that `obj` is mutated in place.
*
* Taken from:
*
* https://github.com/babel/babel/blob/64eafad472ebac6333671fff65a9669739e6cd88/packages/babel-helpers/src/helpers.js#L287-L299
*
* @method defaults
* @param {Object} obj The object to assign the default properties to
* @param {Object} defaults The object that provides all default properties to
* be assigned
* @return {Object} The `obj` that was passed in
*/
export function defaults(obj, defaults) {
const keys = Object.getOwnPropertyNames(defaults);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = Object.getOwnPropertyDescriptor(defaults, key);
if (value && value.configurable && obj[key] === undefined) {
Object.defineProperty(obj, key, value);
}
}
return obj;
}

/**
* Conditionally attempt to polyfill support for `__proto__` in environments
* that do not support it.
*
* If `__proto__` is not supported, this function assigns all properties and
* methods pf `Class.__proto__` to `Class` itself.
*
* @method fixProto
* @param {Function} Class The base class
* @return {Function} The `Class` that was passed in
*/
export default function fixProto(Class) {
if (isProtoSupported) {
return;
}

// https://github.com/babel/babel/tree/64eafad472ebac6333671fff65a9669739e6cd88/packages/babel-plugin-transform-proto-to-assign#example
return defaults(Class, Class.__proto__);
}
5 changes: 5 additions & 0 deletions tests/unit/classes/column-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ test('create column - column instance', function(assert) {
assert.equal(col2.label, 'Name');
});

test('reopen colum', function(assert) {
assert.equal(typeof Column.reopen, 'function', 'reopen is a function');
assert.equal(typeof Column.reopenClass, 'function', 'reopenClass is a function');
});

test('CP - isGroupColumn', function(assert) {
let col = new Column();
assert.ok(col);
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/classes/row-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ test('create row - row instance', function(assert) {
assert.equal(row.get('foo'), 'bar');
assert.equal(row2.get('foo'), 'bar');
});

test('reopen row', function(assert) {
assert.equal(typeof Row.reopen, 'function', 'reopen is a function');
assert.equal(typeof Row.reopenClass, 'function', 'reopenClass is a function');
});
5 changes: 5 additions & 0 deletions tests/unit/classes/table-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ test('create table - with options', function(assert) {
assert.ok(table.columns[0] instanceof Column);
});

test('reopen table', function(assert) {
assert.equal(typeof Table.reopen, 'function', 'reopen is a function');
assert.equal(typeof Table.reopenClass, 'function', 'reopenClass is a function');
});

test('CP - visibleColumnGroups', function(assert) {
let table = new Table();
let col = new Column();
Expand Down
35 changes: 35 additions & 0 deletions tests/unit/utils/fix-proto-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Ember from 'ember';
import fixProto from 'ember-light-table/utils/fix-proto';
import { module, test } from 'qunit';

const {
Object: EmberObject
} = Ember;

module('Unit | Utility | fix proto');

/*
* Running this test in an environment that does not need the polyfill doesn't
* make any sense. The only relevent environments are IE <= 10.
*/

test('ES6 classes that extend `Ember.Object` can be reopened after `fixProto` was used on them', function(assert) {
class DerivedClass extends EmberObject.extend() {}

fixProto(DerivedClass);

DerivedClass.reopenClass({
someStaticMethod() {
return true;
}
});
assert.ok(DerivedClass.someStaticMethod(), 'reopenClass works');

DerivedClass.reopen({
someMethod() {
return true;
}
});
let instance = new DerivedClass();
assert.ok(instance.someMethod(), 'reopen works');
});