Skip to content

Commit

Permalink
Merge pull request #38 from jrsearles/master
Browse files Browse the repository at this point in the history
Use Object.defineProperties in batch
  • Loading branch information
archangel-irk committed Jun 18, 2015
2 parents a1e41c5 + 240cb84 commit 80e6d6c
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 99 deletions.
87 changes: 38 additions & 49 deletions dist/knockout-es5.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
* @param {boolean} propertyNamesOrSettings.deep Use deep track.
* @param {array.<string>} propertyNamesOrSettings.fields Array of property names to wrap.
* todo: @param {array.<string>} propertyNamesOrSettings.exclude Array of exclude property names to wrap.
* todo: @param {function(string, *):boolean} propertyNamesOrSettings.filter Function to filter property names to wrap. A function that takes ... params
* todo: @param {function(string, *):boolean} propertyNamesOrSettings.filter Function to filter property
* names to wrap. A function that takes ... params
* @return {object}
*/
function track(obj, propertyNamesOrSettings) {
Expand All @@ -59,17 +60,10 @@
propertyNamesOrSettings.deep = propertyNamesOrSettings.deep || false;
propertyNamesOrSettings.fields = propertyNamesOrSettings.fields || Object.getOwnPropertyNames(obj);

if (propertyNamesOrSettings.deep === true) {
trackDeep(propertyNamesOrSettings.fields, obj);
} else {
return track(obj, propertyNamesOrSettings.fields);
}

wrap(obj, propertyNamesOrSettings.fields, propertyNamesOrSettings.deep);
} else {
propertyNames = propertyNamesOrSettings || Object.getOwnPropertyNames(obj);
propertyNames.forEach(function(propertyName) {
wrap(propertyName, null, obj);
});
wrap(obj, propertyNames);
}

return obj;
Expand All @@ -84,43 +78,19 @@
return (ctor.toString().trim().match( rFunctionName ) || [])[1];
}

function trackDeep(tree, obj) {
var keys = Array.isArray(tree) ? tree : Object.keys(tree);
var limb;

keys.forEach(function(key) {
limb = obj[key];

wrap(key,
(('Object' === getFunctionName(limb.constructor)
&& Object.keys(limb).length)
? limb
: null),
obj);
});
function canTrack(obj) {
return obj && typeof obj === 'object' && getFunctionName(obj.constructor) === 'Object';
}

// Wrap property into observable
function wrap(prop, subprops, obj) {
var allObservablesForObject = getAllObservablesForObject(obj, true);
var origValue, observable, isArray;

if (subprops) {
origValue = obj[prop];
observable = ko.isObservable(origValue) ? origValue : ko.observable(origValue);

Object.defineProperty(obj, prop, {
configurable: true,
enumerable: true,
get: observable,
set: ko.isWriteableObservable(observable) ? observable : undefined
});

allObservablesForObject[prop] = observable;
function wrap(obj, props, deep) {
if (!props.length) {
return;
}

trackDeep(subprops, obj[prop]);
var allObservablesForObject = getAllObservablesForObject(obj, true);
var descriptor = {};

} else {
props.forEach(function (prop) {
// Skip properties that are already tracked
if (prop in allObservablesForObject) {
return;
Expand All @@ -131,25 +101,44 @@
return;
}

origValue = obj[prop];
isArray = Array.isArray(origValue);
observable = ko.isObservable(origValue) ? origValue
var origValue = obj[prop];
var isObservable = ko.isObservable(origValue);
var isArray = Array.isArray(origValue);
var observable = isObservable ? origValue
: isArray ? ko.observableArray(origValue)
: ko.observable(origValue);

Object.defineProperty(obj, prop, {
// add check in case the object is already an observable array
if (isObservable && 'push' in observable) {
isArray = true;
origValue = observable.peek();
}

descriptor[prop] = {
configurable: true,
enumerable: true,
get: observable,
set: ko.isWriteableObservable(observable) ? observable : undefined
});
};

allObservablesForObject[prop] = observable;

if (isArray) {
notifyWhenPresentOrFutureArrayValuesMutate(ko, observable);

if (deep) {
origValue.forEach(function (child) {
if (canTrack(child)) {
wrap(child, Object.keys(child), true);
}
});
}
} else if (deep && canTrack(origValue)) {
wrap(origValue, Object.keys(origValue), true);
}
}
});

Object.defineProperties(obj, descriptor);
}

function isPlainObject( obj ){
Expand Down
2 changes: 1 addition & 1 deletion dist/knockout-es5.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 38 additions & 49 deletions src/knockout-es5.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
* @param {boolean} propertyNamesOrSettings.deep Use deep track.
* @param {array.<string>} propertyNamesOrSettings.fields Array of property names to wrap.
* todo: @param {array.<string>} propertyNamesOrSettings.exclude Array of exclude property names to wrap.
* todo: @param {function(string, *):boolean} propertyNamesOrSettings.filter Function to filter property names to wrap. A function that takes ... params
* todo: @param {function(string, *):boolean} propertyNamesOrSettings.filter Function to filter property
* names to wrap. A function that takes ... params
* @return {object}
*/
function track(obj, propertyNamesOrSettings) {
Expand All @@ -59,17 +60,10 @@
propertyNamesOrSettings.deep = propertyNamesOrSettings.deep || false;
propertyNamesOrSettings.fields = propertyNamesOrSettings.fields || Object.getOwnPropertyNames(obj);

if (propertyNamesOrSettings.deep === true) {
trackDeep(propertyNamesOrSettings.fields, obj);
} else {
return track(obj, propertyNamesOrSettings.fields);
}

wrap(obj, propertyNamesOrSettings.fields, propertyNamesOrSettings.deep);
} else {
propertyNames = propertyNamesOrSettings || Object.getOwnPropertyNames(obj);
propertyNames.forEach(function(propertyName) {
wrap(propertyName, null, obj);
});
wrap(obj, propertyNames);
}

return obj;
Expand All @@ -84,43 +78,19 @@
return (ctor.toString().trim().match( rFunctionName ) || [])[1];
}

function trackDeep(tree, obj) {
var keys = Array.isArray(tree) ? tree : Object.keys(tree);
var limb;

keys.forEach(function(key) {
limb = obj[key];

wrap(key,
(('Object' === getFunctionName(limb.constructor)
&& Object.keys(limb).length)
? limb
: null),
obj);
});
function canTrack(obj) {
return obj && typeof obj === 'object' && getFunctionName(obj.constructor) === 'Object';
}

// Wrap property into observable
function wrap(prop, subprops, obj) {
var allObservablesForObject = getAllObservablesForObject(obj, true);
var origValue, observable, isArray;

if (subprops) {
origValue = obj[prop];
observable = ko.isObservable(origValue) ? origValue : ko.observable(origValue);

Object.defineProperty(obj, prop, {
configurable: true,
enumerable: true,
get: observable,
set: ko.isWriteableObservable(observable) ? observable : undefined
});

allObservablesForObject[prop] = observable;
function wrap(obj, props, deep) {
if (!props.length) {
return;
}

trackDeep(subprops, obj[prop]);
var allObservablesForObject = getAllObservablesForObject(obj, true);
var descriptor = {};

} else {
props.forEach(function (prop) {
// Skip properties that are already tracked
if (prop in allObservablesForObject) {
return;
Expand All @@ -131,25 +101,44 @@
return;
}

origValue = obj[prop];
isArray = Array.isArray(origValue);
observable = ko.isObservable(origValue) ? origValue
var origValue = obj[prop];
var isObservable = ko.isObservable(origValue);
var isArray = Array.isArray(origValue);
var observable = isObservable ? origValue
: isArray ? ko.observableArray(origValue)
: ko.observable(origValue);

Object.defineProperty(obj, prop, {
// add check in case the object is already an observable array
if (isObservable && 'push' in observable) {
isArray = true;
origValue = observable.peek();
}

descriptor[prop] = {
configurable: true,
enumerable: true,
get: observable,
set: ko.isWriteableObservable(observable) ? observable : undefined
});
};

allObservablesForObject[prop] = observable;

if (isArray) {
notifyWhenPresentOrFutureArrayValuesMutate(ko, observable);

if (deep) {
origValue.forEach(function (child) {
if (canTrack(child)) {
wrap(child, Object.keys(child), true);
}
});
}
} else if (deep && canTrack(origValue)) {
wrap(origValue, Object.keys(origValue), true);
}
}
});

Object.defineProperties(obj, descriptor);
}

function isPlainObject( obj ){
Expand Down
14 changes: 14 additions & 0 deletions test/arrays.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,18 @@ describe('Array handling', function () {
assert.equal(allNotifiedValues.length, 1);
assert.equal(allNotifiedValues[ 0 ], '4,5');
});

it('if a property is already an observable array, it still gets wrapped with mutator functions', function () {
var plainArray = ['a', 'b', 'c'],
obj = ko.track({ myArray: ko.observableArray(plainArray) }),
lastNotifiedValue = ko.computed(function() { return obj.myArray.join(','); });

// Reading the property returns the underlying array value
assert.equal(obj.myArray instanceof Array, true);
assert.deepEqual(obj.myArray, plainArray);
assert.equal(lastNotifiedValue(), 'a,b,c');

obj.myArray.push('d');
assert.equal(lastNotifiedValue(), 'a,b,c,d');
});
});

0 comments on commit 80e6d6c

Please sign in to comment.