can-reflect
Advanced tools
Comparing version 1.0.0-pre.2 to 1.0.0-pre.3
@page can-reflect | ||
@parent can-infrastructure | ||
@@ -3,0 +4,0 @@ # can-reflect |
{ | ||
"name": "can-reflect", | ||
"version": "1.0.0-pre.2", | ||
"version": "1.0.0-pre.3", | ||
"description": "reflection on unknown data types", | ||
@@ -5,0 +5,0 @@ "homepage": "http://canjs.com", |
var canSymbol = require("can-symbol"); | ||
var typeReflections = require("../type/type"); | ||
/** | ||
* @module can-reflect/call Call | ||
* @parent can-reflect | ||
* | ||
* @description a set of functions that deal with callable objects. Generally these are functions, but other | ||
* object types can implement these to make them callable with [can-reflect] | ||
*/ | ||
module.exports = { | ||
/** | ||
* @function {function(...), Object, ...} can-reflect/call.call call | ||
* @parent can-reflect/call | ||
* @description Call a callable, with a context object and parameters | ||
* | ||
* @signature `call(func, context, ...rest)` | ||
* | ||
* Call the callable `func` as if it were a function, bound to `context` and with any additional parameters | ||
* occurring after `context` set to the positional parameters. | ||
* | ||
* Note that `func` *must* either be natively callable, implement [can-symbol/symbols/apply @@@@can.apply], | ||
* or have a callable `apply` property to work with `canReflect.call` | ||
* | ||
* ``` | ||
* var compute = canCompute("foo"); | ||
* | ||
* canReflect.call(compute, null, "bar"); | ||
* canReflect.call(compute, null); // -> "bar" | ||
* ``` | ||
* | ||
* @param {function(...)} func the function to call with the supplied arguments | ||
* @param {Object} context the context object to set as `this` on the function call | ||
* @param {*} rest any arguments after `context` will be passed to the function call | ||
* @return {*} return types and values are determined by the call to `func` | ||
*/ | ||
call: function(func, context){ | ||
@@ -14,2 +46,27 @@ var args = [].slice.call(arguments, 2); | ||
}, | ||
/** | ||
* @function {function(...), Object, ...} can-reflect/call.apply apply | ||
* @parent can-reflect/call | ||
* @description Call a callable, with a context object and a list of parameters | ||
* | ||
* @signature `apply(func, context, args)` | ||
* | ||
* Call the callable `func` as if it were a function, bound to `context` and with any additional parameters | ||
* contained in the Array-like `args` | ||
* | ||
* Note that `func` *must* either be natively callable, implement [can-symbol/symbols/apply @@@@can.apply], | ||
* or have a callable `apply` property to work with `canReflect.apply` | ||
* | ||
* ``` | ||
* var compute = canCompute("foo"); | ||
* | ||
* canReflect.apply(compute, null, ["bar"]); | ||
* canReflect.apply(compute, null, []); // -> "bar" | ||
* ``` | ||
* | ||
* @param {function(...)} func the function to call | ||
* @param {Object} context the context object to set as `this` on the function call | ||
* @param {*} args arguments to be passed to the function call | ||
* @return {*} return types and values are determined by the call to `func` | ||
*/ | ||
apply: function(func, context, args){ | ||
@@ -23,2 +80,23 @@ var apply = func[canSymbol.for("can.apply")]; | ||
}, | ||
/** | ||
* @function {function(...), ...} can-reflect/call.new new | ||
* @parent can-reflect/call | ||
* @description Construct a new instance of a callable constructor | ||
* | ||
* @signature `new(func, ...rest)` | ||
* | ||
* Call the callable `func` as if it were a function, bound to a new instance of `func`, and with any additional | ||
* parameters occurring after `func` set to the positional parameters. | ||
* | ||
* Note that `func` *must* either implement [can-symbol/symbols/new @@@@can.new], | ||
* or have a callable `apply` property *and* a prototype to work with `canReflect.new` | ||
* | ||
* ``` | ||
* canReflect.new(DefineList, ["foo"]); // -> ["foo"]<DefineList> | ||
* ``` | ||
* | ||
* @param {function(...)} func a constructor | ||
* @param {*} rest arguments to be passed to the constructor | ||
* @return {Object} if `func` returns an Object, that returned Object; otherwise a new instance of `func` | ||
*/ | ||
"new": function(func){ | ||
@@ -25,0 +103,0 @@ var args = [].slice.call(arguments, 1); |
@@ -9,3 +9,29 @@ var canSymbol = require("can-symbol"); | ||
/** | ||
* @module can-reflect/get-set Get/Set | ||
* @parent can-reflect | ||
* @description Reflection functions for getters and setters on MapLikes, ListLikes, and ValueLikes. | ||
*/ | ||
var reflections = { | ||
/** | ||
* @function {Object, String, *} can-reflect/get-set.setKeyValue setKeyValue | ||
* @parent can-reflect/get-set | ||
* @description Set the value of a named property on a MapLike object. | ||
* | ||
* @signature `setKeyValue(obj, key, value)` | ||
* | ||
* Set the property on Map-like `obj`, identified by the String or Symbol value `key`, to the value `value`. | ||
* The default behavior can be overridden on `obj` by implementing [can-symbol/symbols/setKeyValue @@@@can.setKeyValue], | ||
* otherwise native named property access is used for string keys, and `Object.defineProperty` is used to set symbols. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ bar: "baz" }); | ||
* | ||
* canReflect.setKeyValue(foo, "bar", "quux"); | ||
* foo[bar]; // -> "quux" | ||
* ``` | ||
* @param {Object} obj the object to set on | ||
* @param {String} key the key for the property to set | ||
* @param {*} value the value to set on the object | ||
*/ | ||
setKeyValue: function(obj, key, value){ | ||
@@ -31,2 +57,22 @@ if(typeof key === "symbol") { | ||
}, | ||
/** | ||
* @function {Object, String} can-reflect/get-set.getKeyValue getKeyValue | ||
* @parent can-reflect/get-set | ||
* @description Get the value of a named property on a MapLike object. | ||
* | ||
* @signature `getKeyValue(obj, key)` | ||
* | ||
* Retrieve the property on Map-like `obj` identified by the String or Symbol value `key`. The default behavior | ||
* can be overridden on `obj` by implementing [can-symbol/symbols/getKeyValue @@@@can.getKeyValue], | ||
* otherwise native named property access is used. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ bar: "baz" }); | ||
* | ||
* canReflect.getKeyValue(foo, "bar"); // -> "baz" | ||
* ``` | ||
* | ||
* @param {Object} obj the object to get from | ||
* @param {String} key the key of the property to get | ||
*/ | ||
getKeyValue: function(obj, key) { | ||
@@ -39,2 +85,31 @@ var getKeyValue = obj[getKeyValueSymbol]; | ||
}, | ||
/** | ||
* @function {Object, String} can-reflect/get-set.deleteKeyValue deleteKeyValue | ||
* @parent can-reflect/get-set | ||
* @description Delete a named property from a MapLike object. | ||
* | ||
* @signature `deleteKeyValue(obj, key)` | ||
* | ||
* Remove the property identified by the String or Symbol `key` from the Map-like object `obj`, if possible. | ||
* Property definitions may interfere with deleting key values; the behavior on `obj` if `obj[key]` cannot | ||
* be deleted is undefined. The default use of the native `delete` keyword can be overridden by `obj` if it | ||
* implements [can-symbol/symbols/deleteKeyValue @@@@can.deleteKeyValue]. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ bar: "baz" }); | ||
* var quux = new CanMap({ thud: "jeek" }); | ||
* | ||
* canReflect.deleteKeyValue(foo, "bar"); | ||
* canReflect.deleteKeyValue(quux, "thud"); | ||
* | ||
* "bar" in foo; // -> true -- DefineMaps use property defs which cannot be un-defined | ||
* foo.bar // -> undefined -- but set values to undefined when deleting | ||
* | ||
* "thud" in quux; // -> false | ||
* quux.thud; // -> undefined | ||
* ``` | ||
* | ||
* @param {Object} obj the object to delete on | ||
* @param {String} key the key for the property to delete | ||
*/ | ||
deleteKeyValue: function(obj, key) { | ||
@@ -47,2 +122,25 @@ var deleteKeyValue = obj[canSymbol.for("can.deleteKeyValue")]; | ||
}, | ||
/** | ||
* @function {Object} can-reflect/get-set.getValue getValue | ||
* @parent can-reflect/get-set | ||
* @description Get the value of an object with a gettable value | ||
* | ||
* @signature `getValue(obj)` | ||
* | ||
* Return the value of the Value-like object `obj`. Unless `obj` implements | ||
* [can-symbol/symbols/getValue @@@@can.getValue], the result of `getValue` on | ||
* `obj` will always be `obj`. Observable Map-like objects may want to implement | ||
* `@@@@can.getValue` to return non-observable or plain representations of themselves. | ||
* | ||
* ``` | ||
* var compute = canCompute("foo"); | ||
* var primitive = "bar"; | ||
* | ||
* canReflect.getValue(compute); // -> "foo" | ||
* canReflect.getValue(primitive); // -> "bar" | ||
* ``` | ||
* | ||
* @param {Object} obj the object to get from | ||
* @return {*} the value of the object via `@@can.getValue`, or the value itself. | ||
*/ | ||
getValue: function(value){ | ||
@@ -58,2 +156,27 @@ if(typeReflections.isPrimitive(value)) { | ||
}, | ||
/** | ||
* @function {Object, *} can-reflect/get-set.setValue setValue | ||
* @parent can-reflect/get-set | ||
* @description Set the value of a mutable object. | ||
* | ||
* @signature `setValue(obj, value)` | ||
* | ||
* Set the value of a Value-like object `obj` to the value `value`. `obj` *must* implement | ||
* [can-symbol/symbols/setValue @@@@can.setValue] to be used with `canReflect.setValue`. | ||
* Map-like objects may want to implement `@@@@can.setValue` to merge objects of properties | ||
* into themselves. | ||
* | ||
* ``` | ||
* var compute = canCompute("foo"); | ||
* var plain = {}; | ||
* | ||
* canReflect.setValue(compute, "bar"); | ||
* compute(); // -> bar | ||
* | ||
* canReflect.setValue(plain, { quux: "thud" }); // throws "can-reflect.setValue - Can not set value." | ||
* ``` | ||
* | ||
* @param {Object} obj the object to set on | ||
* @param {*} value the value to set for the object | ||
*/ | ||
setValue: function(item, value){ | ||
@@ -68,6 +191,18 @@ var setValue = item && item[setValueSymbol]; | ||
}; | ||
/** | ||
* @function {Object, String} can-reflect/get-set.get get | ||
* @description an alias for [can-reflect/get-set.getKeyValue getKeyValue] | ||
*/ | ||
reflections.get = reflections.getKeyValue; | ||
/** | ||
* @function {Object, String} can-reflect/get-set.set set | ||
* @description an alias for [can-reflect/get-set.setKeyValue setKeyValue] | ||
*/ | ||
reflections.set = reflections.setKeyValue; | ||
reflections.delete = reflections.deleteKeyValue; | ||
/** | ||
* @function {Object, String} can-reflect/get-set.delete delete | ||
* @description an alias for [can-reflect/get-set.deleteKeyValue deleteKeyValue] | ||
*/ | ||
reflections["delete"] = reflections.deleteKeyValue; | ||
module.exports = reflections; |
@@ -25,16 +25,219 @@ var canSymbol = require("can-symbol"); | ||
/** | ||
* @module can-reflect/observe Observe | ||
* @parent can-reflect | ||
* | ||
* The `Observe` suite of reflections covers event binding for key values on observable MapLike objects, and for | ||
* the values of observable ValueLike objects. | ||
*/ | ||
module.exports = { | ||
// KEY | ||
/** | ||
* @function {Object, String, function(*, *)} can-reflect/observe.onKeyValue onKeyValue | ||
* @parent can-reflect/observe | ||
* @description Register an event handler on a MapLike object, based on a key change | ||
* | ||
* @signature `onKeyValue(obj, key, handler)` | ||
* | ||
* Register a handler on the Map-like object `obj` to trigger when the property key `key` changes. | ||
* `obj` *must* implement [can-symbol/symbols/onKeyValue @@@@can.onKeyValue] to be compatible with | ||
* can-reflect.onKeyValue. The function passed as `handler` will receive the new value of the property | ||
* as the first argument, and the previous value of the property as the second argument. | ||
* | ||
* ``` | ||
* var obj = new DefineMap({ foo: "bar" }); | ||
* canReflect.onKeyValue(obj, "foo", function(newVal, oldVal) { | ||
* console.log("foo is now", newVal, ", was", oldVal); | ||
* }); | ||
* | ||
* obj.foo = "baz"; // -> logs "foo is now baz , was bar" | ||
* ``` | ||
* | ||
* @param {Object} obj an observable MapLike that can listen to changes in named properties. | ||
* @param {String} key the key to listen to | ||
* @param {function(*, *)} handler a callback function that recieves the new value | ||
*/ | ||
onKeyValue: makeFallback("can.onKeyValue", "onEvent"), | ||
/** | ||
* @function {Object, String, function(*)} can-reflect/observe.offKeyValue offKeyValue | ||
* @parent can-reflect/observe | ||
* @description Unregister an event handler on a MapLike object, based on a key change | ||
* | ||
* @signature `offKeyValue(obj, key, handler)` | ||
* | ||
* Unregister a handler from the Map-like object `obj` that had previously been registered with | ||
* [can-reflect/observe.onKeyValue onKeyValue]. The function passed as `handler` will no longer be called | ||
* when the value of `key` on `obj` changes. | ||
* | ||
* ``` | ||
* var obj = new DefineMap({ foo: "bar" }); | ||
* var handler = function(newVal, oldVal) { | ||
* console.log("foo is now", newVal, ", was", oldVal); | ||
* }; | ||
* | ||
* canReflect.onKeyValue(obj, "foo", handler); | ||
* canReflect.offKeyValue(obj, "foo", handler); | ||
* | ||
* obj.foo = "baz"; // -> nothing is logged | ||
* ``` | ||
* | ||
* @param {Object} obj an observable MapLike that can listen to changes in named properties. | ||
* @param {String} key the key to stop listening to | ||
* @param {function(*)} handler the callback function that should be removed from the event handlers for `key` | ||
*/ | ||
offKeyValue: makeFallback("can.offKeyValue","offEvent"), | ||
/** | ||
* @function {Object, function(Array)} can-reflect/observe.onKeys onKeys | ||
* @parent can-reflect/observe | ||
* @description Register an event handler on a MapLike object, triggered on the key set changing | ||
* | ||
* @signature `onKeys(obj, handler)` | ||
* | ||
* Register an event handler on the Map-like object `obj` to trigger when `obj`'s keyset changes. | ||
* `obj` *must* implement [can-symbol/symbols/onKeys @@@@can.onKeys] to be compatible with | ||
* can-reflect.onKeys. The function passed as `handler` will receive an Array of object diffs (see | ||
* [can-util/js/diff-object/diff-object diffObject] for the format) as its one argument. | ||
* | ||
* ``` | ||
* var obj = new DefineMap({ foo: "bar" }); | ||
* canReflect.onKeys(obj, function(diffs) { | ||
* console.log(diffs); | ||
* }); | ||
* | ||
* obj.set("baz", "quux"); // -> logs '[{"property": "baz", "type": "add", "value": "quux"}]' | ||
* ``` | ||
* | ||
* @param {Object} obj an observable MapLike that can listen to changes in named properties. | ||
* @param {function(Array)} handler the callback function to receive the diffs in the key set | ||
*/ | ||
// any key change (diff would normally happen) | ||
onKeys: makeErrorIfMissing("can.onKeys","can-reflect: can not observe an onKeys event"), | ||
/** | ||
* @function {Object, function(Array)} can-reflect/observe.onKeysAdded onKeysAdded | ||
* @parent can-reflect/observe | ||
* @description Register an event handler on a MapLike object, triggered on new keys being added. | ||
* | ||
* @signature `onKeysAdded(obj, handler)` | ||
* | ||
* Register an event handler on the Map-like object `obj` to trigger when a new key or keys are set on | ||
* `obj`. `obj` *must* implement [can-symbol/symbols/onKeysAdded @@@@can.onKeysAdded] to be compatible with | ||
* can-reflect.onKeysAdded. The function passed as `handler` will receive an Array of Strings as its one | ||
* argument. | ||
* | ||
* ``` | ||
* var obj = new DefineMap({ foo: "bar" }); | ||
* canReflect.onKeysAded(obj, function(newKeys) { | ||
* console.log(newKeys); | ||
* }); | ||
* | ||
* foo.set("baz", "quux"); // -> logs '["baz"]' | ||
* ``` | ||
* | ||
* @param {Object} obj an observable MapLike that can listen to changes in named properties. | ||
* @param {function(Array)} handler the callback function to receive the array of added keys | ||
*/ | ||
// keys added at a certain point {key: 1}, index | ||
onKeysAdded: makeErrorIfMissing("can.onKeysAdded","can-reflect: can not observe an onKeysAdded event"), | ||
/** | ||
* @function {Object, function(Array)} can-reflect/observe.onKeysRemoved onKeysRemoved | ||
* @parent can-reflect/observe | ||
* @description Register an event handler on a MapLike object, triggered on keys being deleted. | ||
* | ||
* @signature `onKeysRemoved(obj, handler)` | ||
* | ||
* Register an event handler on the Map-like object `obj` to trigger when a key or keys are removed from | ||
* `obj`'s keyset. `obj` *must* implement [can-symbol/symbols/onKeysRemoved @@@@can.onKeysRemoved] to be | ||
* compatible with can-reflect.onKeysAdded. The function passed as `handler` will receive an Array of | ||
* Strings as its one argument. | ||
* | ||
* ``` | ||
* var obj = new CanMap({ foo: "bar" }); | ||
* canReflect.onKeys(obj, function(diffs) { | ||
* console.log(JSON.stringify(diffs)); | ||
* }); | ||
* | ||
* foo.removeAttr("foo"); // -> logs '["foo"]' | ||
* ``` | ||
* | ||
* @param {Object} obj an observable MapLike that can listen to changes in named properties. | ||
* @param {function(Array)} handler the callback function to receive the array of removed keys | ||
*/ | ||
onKeysRemoved: makeErrorIfMissing("can.onKeysRemoved","can-reflect: can not unobserve an onKeysRemoved event"), | ||
/** | ||
* @function {Object, String} can-reflect/observe.getKeyDependencies getKeyDependencies | ||
* @parent can-reflect/observe | ||
* @description Return the observable objects that compute to the value of a named property on an object | ||
* | ||
* @signature `getKeyDependencies(obj, key)` | ||
* | ||
* Return the observable objects that provide input values to generate the computed value of the | ||
* property `key` on Map-like object `obj`. If `key` does not have dependencies on `obj`, returns `undefined`. | ||
* Otherwise returns an object with up to two keys: `keyDependencies` is a [can-util/js/cid-map/cid-map CIDMap] that | ||
* maps each Map-like object providing keyed values to an Array of the relevant keys; `valueDependencies` is a | ||
* [can-util/js/cid-set/cid-set CIDSet] that contains all Value-like dependencies providing their own values. | ||
* | ||
* `obj` *must* implement [can-symbol/symbols/getKeyDependencies @@@@can.getKeyDependencies] to work with | ||
* `canReflect.getKeyDependencies`. | ||
* | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ "bar": "baz" }) | ||
* var obj = new (DefineMap.extend({ | ||
* baz: { | ||
* get: function() { | ||
* return foo.bar; | ||
* } | ||
* } | ||
* }))(); | ||
* | ||
* canReflect.getKeyDependencies(obj, "baz"); // -> { valueDependencies: CIDSet } | ||
* ``` | ||
* | ||
* @param {Object} obj the object to check for key dependencies | ||
* @param {String} key the key on the object to check | ||
* @return {Object} the observable values that this keyed value depends on | ||
*/ | ||
getKeyDependencies: makeErrorIfMissing("can.getKeyDependencies","can-reflect: can not determine dependencies"), | ||
/** | ||
* @function {Object, String} can-reflect/observe.keyHasDependencies keyHasDependencies | ||
* @parent can-reflect/observe | ||
* @description Determine whether the value for a named property on an object is bound to other events | ||
* | ||
* @signature `keyHasDependencies(obj, key)` | ||
* | ||
* Returns `true` if the computed value of the property `key` on Map-like object `obj` derives from other values. | ||
* Returns `false` if `key` is computed on `obj` but does not have dependencies on other objects. If `key` is not | ||
* a computed value on `obj`, returns `undefined`. | ||
* | ||
* `obj` *must* implement [can-symbol/symbols/keyHasDependencies @@@@can.keyHasDependencies] to work with | ||
* `canReflect.keyHasDependencies`. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ "bar": "baz" }) | ||
* var obj = new (DefineMap.extend({ | ||
* baz: { | ||
* get: function() { | ||
* return foo.bar; | ||
* } | ||
* }, | ||
* quux: { | ||
* get: function() { | ||
* return "thud"; | ||
* } | ||
* } | ||
* }))(); | ||
* | ||
* canReflect.keyHasDependencies(obj, "baz"); // -> true | ||
* canReflect.keyHasDependencies(obj, "quux"); // -> false | ||
* canReflect.keyHasDependencies(foo, "bar"); // -> undefined | ||
* ``` | ||
* | ||
* @param {Object} obj the object to check for key dependencies | ||
* @param {String} key the key on the object to check | ||
* @return {Boolean} `true` if there are other objects that may update the keyed value; `false` otherwise | ||
* | ||
*/ | ||
// TODO: use getKeyDeps once we know what that needs to look like | ||
@@ -44,11 +247,155 @@ keyHasDependencies: makeErrorIfMissing("can.keyHasDependencies","can-reflect: can not determine if this has key dependencies"), | ||
// VALUE | ||
/** | ||
* @function {Object, function(*)} can-reflect/observe.onValue onValue | ||
* @parent can-reflect/observe | ||
* @description Register an event handler on an observable ValueLike object, based on a change in its value | ||
* | ||
* @signature `onValue(handler)` | ||
* | ||
* Register an event handler on the Value-like object `obj` to trigger when its value changes. | ||
* `obj` *must* implement [can-symbol/symbols/onValue @@@@can.onValue] to be compatible with | ||
* can-reflect.onKeyValue. The function passed as `handler` will receive the new value of `obj` | ||
* as the first argument, and the previous value of `obj` as the second argument. | ||
* | ||
* ``` | ||
* var obj = canCompute("foo"); | ||
* canReflect.onValue(obj, function(newVal, oldVal) { | ||
* console.log("compute is now", newVal, ", was", oldVal); | ||
* }); | ||
* | ||
* obj("bar"); // -> logs "compute is now bar , was foo" | ||
* ``` | ||
* | ||
* @param {*} obj any object implementing @@can.onValue | ||
* @param {function(*, *)} handler a callback function that receives the new and old values | ||
*/ | ||
onValue: makeErrorIfMissing("can.onValue","can-reflect: can not observe value change"), | ||
/** | ||
* @function {Object, function(*)} can-reflect/observe.offValue offValue | ||
* @parent can-reflect/observe | ||
* @description Unregister an value change handler from an observable ValueLike object | ||
* | ||
* @signature `offValue(handler)` | ||
* | ||
* Unregister an event handler from the Value-like object `obj` that had previously been registered with | ||
* [can-reflect/observe.onValue onValue]. The function passed as `handler` will no longer be called | ||
* when the value of `obj` changes. | ||
* | ||
* ``` | ||
* var obj = canCompute( "foo" ); | ||
* var handler = function(newVal, oldVal) { | ||
* console.log("compute is now", newVal, ", was", oldVal); | ||
* }; | ||
* | ||
* canReflect.onKeyValue(obj, handler); | ||
* canReflect.offKeyValue(obj, handler); | ||
* | ||
* obj("baz"); // -> nothing is logged | ||
* ``` | ||
* | ||
* @param {*} obj | ||
* @param {function(*)} handler | ||
*/ | ||
offValue: makeErrorIfMissing("can.offValue","can-reflect: can not unobserve value change"), | ||
/** | ||
* @function {Object} can-reflect/observe.getValueDependencies getValueDependencies | ||
* @parent can-reflect/observe | ||
* @description Return all the events that bind to the value of an observable, Value-like object | ||
* | ||
* @signature `getValueDependencies(obj)` | ||
* | ||
* Return the observable objects that provide input values to generate the computed value of the | ||
* Value-like object `obj`. If `obj` does not have dependencies, returns `undefined`. | ||
* Otherwise returns an object with up to two keys: `keyDependencies` is a [can-util/js/cid-map/cid-map CIDMap] that | ||
* maps each Map-like object providing keyed values to an Array of the relevant keys; `valueDependencies` is a | ||
* [can-util/js/cid-set/cid-set CIDSet] that contains all Value-like dependencies providing their own values. | ||
* | ||
* `obj` *must* implement [can-symbol/symbols/getValueDependencies @@@@can.getValueDependencies] to work with | ||
* `canReflect.getValueDependencies`. | ||
* | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ "bar": "baz" }) | ||
* var obj = canCompute(function() { | ||
* return foo.bar; | ||
* }); | ||
* | ||
* canReflect.getValueDependencies(obj); // -> { valueDependencies: CIDSet } because `obj` is internally backed by | ||
* a [can-observation] | ||
* ``` | ||
* | ||
* @param {Object} obj the object to check for value dependencies | ||
* @return {Object} the observable objects that `obj`'s value depends on | ||
* | ||
*/ | ||
getValueDependencies: makeErrorIfMissing("can.getValueDependencies","can-reflect: can not determine dependencies"), | ||
/** | ||
* @function {Object} can-reflect/observe.valueHasDependencies valueHasDependencies | ||
* @parent can-reflect/observe | ||
* @description Determine whether the value of an observable object is bound to other events | ||
* | ||
* @signature `valueHasDependencies(obj)` | ||
* | ||
* Returns `true` if the computed value of the Value-like object `obj` derives from other values. | ||
* Returns `false` if `obj` is computed but does not have dependencies on other objects. If `obj` is not | ||
* a computed value, returns `undefined`. | ||
* | ||
* `obj` *must* implement [can-symbol/symbols/valueHasDependencies @@@@can.valueHasDependencies] to work with | ||
* `canReflect.valueHasDependencies`. | ||
* | ||
* ``` | ||
* var foo = canCompute( "bar" ); | ||
* var baz = canCompute(function() { | ||
* return foo(); | ||
* }); | ||
* var quux = "thud"; | ||
* var jeek = canCompute(function(plonk) { | ||
* if(argument.length) { | ||
* quux = plonk; | ||
* } | ||
* return quux; | ||
* }); | ||
* | ||
* canReflect.valueHasDependencies(baz); // -> true | ||
* canReflect.valueHasDependencies(jeek); // -> false | ||
* canReflect.valueHasDependencies(foo); // -> undefined | ||
* ``` | ||
* | ||
* @param {Object} obj the object to check for dependencies | ||
* @return {Boolean} `true` if there are other dependencies that may update the object's value; `false` otherwise | ||
* | ||
*/ | ||
// TODO: use getValueDeps once we know what that needs to look like | ||
valueHasDependencies: makeErrorIfMissing("can.valueHasDependencies","can-reflect: can not determine if value has dependencies"), | ||
// EVENT | ||
/** | ||
* @function {Object, String, function(*)} can-reflect/observe.onEvent onEvent | ||
* @parent can-reflect/observe | ||
* @description Register a named event handler on an observable object | ||
* | ||
* @signature `onEvent(obj, eventName, callback)` | ||
* | ||
* | ||
* Register an event handler on the object `obj` to trigger when the event `eventName` is dispatched. | ||
* `obj` *must* implement [can-symbol/symbols/onKeyValue @@@@can.onEvent] or `.addEventListener()` to be compatible | ||
* with can-reflect.onKeyValue. The function passed as `callback` will receive the event descriptor as the first | ||
* argument, and any data passed to the event dispatch as subsequent arguments. | ||
* | ||
* ``` | ||
* var obj = new DefineMap({ foo: "bar" }); | ||
* canReflect.onEvent(obj, "foo", function(ev, newVal, oldVal) { | ||
* console.log("foo is now", newVal, ", was", oldVal); | ||
* }); | ||
* | ||
* canEvent.dispatch.call(obj, "foo", ["baz", "quux"]); // -> logs "foo is now baz , was quux" | ||
* ``` | ||
* | ||
* @param {Object} obj the object to bind a new event handler to | ||
* @param {String} eventName the name of the event to bind the handler to | ||
* @param {function(*)} callback the handler function to bind to the event | ||
*/ | ||
onEvent: function(obj, eventName, callback){ | ||
@@ -64,2 +411,29 @@ if(obj) { | ||
}, | ||
/** | ||
* @function {Object, String, function(*)} can-reflect/observe.offValue offEvent | ||
* @parent can-reflect/observe | ||
* @description Unregister an event handler on a MapLike object, based on a key change | ||
* | ||
* @signature `offKeyValue(obj, eventName, callback)` | ||
* | ||
* Unregister an event handler from the object `obj` that had previously been registered with | ||
* [can-reflect/observe.onEvent onEvent]. The function passed as `callback` will no longer be called | ||
* when the event named `eventName` is dispatched on `obj`. | ||
* | ||
* ``` | ||
* var obj = new DefineMap({ foo: "bar" }); | ||
* var handler = function(ev, newVal, oldVal) { | ||
* console.log("foo is now", newVal, ", was", oldVal); | ||
* }; | ||
* | ||
* canReflect.onEvent(obj, "foo", handler); | ||
* canReflect.offKeyValue(obj, "foo", handler); | ||
* | ||
* canEvent.dispatch.call(obj, "foo", ["baz", "quux"]); // -> nothing is logged | ||
* ``` | ||
* | ||
* @param {Object} obj the object to unbind an event handler from | ||
* @param {String} eventName the name of the event to unbind the handler from | ||
* @param {function(*)} callback the handler function to unbind from the event | ||
*/ | ||
offEvent: function(obj, eventName, callback){ | ||
@@ -66,0 +440,0 @@ if(obj) { |
@@ -5,3 +5,36 @@ var canSymbol = require("can-symbol"); | ||
/** | ||
* @module can-reflect/shape Shape | ||
* @type {Object} | ||
* @description Reflections based on available and enumerable keys on an object | ||
* | ||
*/ | ||
var shapeReflections = { | ||
/** | ||
* @function {Object, function(*), [Object]} can-reflect/shape.each each | ||
* @parent can-reflect/shape | ||
* @description Iterate a List-like or Map-like, calling `callback` on each keyed or indexed property | ||
* | ||
* @signature `each(obj, callback, context)` | ||
* | ||
* If `obj` is a List-like or an Iterator-like, `each` functions as [can-reflect/shape.eachIndex eachIndex], | ||
* iterating over numeric indexes from 0 to `obj.length - 1` and calling `callback` with each property and | ||
* index, optionally with `context` as `this` (defaulting to `obj`). If not, `each` functions as | ||
* [can-reflect/shape.eachKey eachKey], | ||
* iterating over every Number and String key on `obj` and calling `callback` on each one. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ bar: "baz" }); | ||
* var quux = new DefineList([ "thud", "jeek" ]); | ||
* | ||
* canReflect.each(foo, console.log, console); // -> logs 'baz bar {foo}' | ||
* canReflect.each(quux, console.log, console); // -> logs 'thud 0 {quux}'; logs 'jeek 1 {quux}' | ||
* ``` | ||
* | ||
* @param {Object} obj The object to iterate over | ||
* @param {Function(*, ValueLike)} callback a function that receives each item in the ListLike or MapLike | ||
* @param {[Object]} context an optional `this` context for calling the callback | ||
* @return {Array} the result of calling [can-reflect/shape.eachIndex `eachIndex`] if `obj` is a ListLike, | ||
* or [can-reflect/shape.eachKey `eachKey`] if a MapLike. | ||
*/ | ||
each: function(obj, callback, context){ | ||
@@ -17,4 +50,26 @@ | ||
// each index in something list-like. Uses iterator if it has it. | ||
/** | ||
* @function {ListLike, function(*), [Object]} can-reflect/shape.eachIndex eachIndex | ||
* @parent can-reflect/shape | ||
* @description Iterate a ListLike calling `callback` on each numerically indexed element | ||
* | ||
* @signature `eachIndex(list, callback, context)` | ||
* | ||
* For each numeric index from 0 to `list.length - 1`, call `callback`, passing the current | ||
* property value, the current index, and `list`, and optionally setting `this` as `context` | ||
* if specified (otherwise use the current property value). | ||
* | ||
* ``` | ||
* var foo = new DefineList([ "bar", "baz" ]); | ||
* | ||
* canReflect.eachIndex(foo, console.log, console); // -> logs 'bar 0 {foo}'; logs 'baz 1 {foo}' | ||
* ``` | ||
* | ||
* @param {ListLike} list The list to iterate over | ||
* @param {Function(*, Number)} callback a function that receives each item | ||
* @param {[Object]} context an optional `this` context for calling the callback | ||
* @return {ListLike} the original list | ||
*/ | ||
eachIndex: function(list, callback, context){ | ||
// each index in something list-like. Uses iterator if it has it. | ||
var iter, iterator = list[canSymbol.iterator]; | ||
@@ -48,2 +103,23 @@ if(Array.isArray(list)) { | ||
}, | ||
/** | ||
* @function can-reflect/shape.toArray toArray | ||
* @parent can-reflect/shape | ||
* @description convert the values of any MapLike or ListLike into an array | ||
* | ||
* @signature `toArray(obj)` | ||
* | ||
* Convert the values of any Map-like or List-like into a JavaScript Array. If a Map-like, | ||
* key data is discarded and only value data is preserved. | ||
* | ||
* ``` | ||
* var foo = new DefineList(["bar", "baz"]); | ||
* var quux = new DefineMap({ thud: "jeek" }); | ||
* ``` | ||
* | ||
* canReflect.toArray(foo); // -> ["bar", "baz"] | ||
* canReflect.toArray(quux): // -> ["jeek"] | ||
* | ||
* @param {Object} obj Any object, whether MapLike or ListLike | ||
* @return {Array} an array of the values of `obj` | ||
*/ | ||
toArray: function(obj){ | ||
@@ -56,5 +132,28 @@ var arr = []; | ||
}, | ||
// each key in something map like | ||
// eachOwnEnumerableKey | ||
/** | ||
* @function can-reflect/shape.eachKey eachKey | ||
* @parent can-reflect/shape | ||
* @description Iterate over a MapLike, calling `callback` on each enumerable property | ||
* | ||
* @signature `eachKey(obj, callback, context)` | ||
* | ||
* Iterate all own enumerable properties on Map-like `obj` | ||
* (using [can-reflect/shape/getOwnEnumerableKeys canReflect.getOwnEnumerableKeys]), and call | ||
* `callback` with the property value, the property key, and `obj`, and optionally setting | ||
* `this` on the callback as `context` if provided, `obj` otherwise. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ bar: "baz" }); | ||
* | ||
* canReflect.eachKey(foo, console.log, console); // logs 'baz bar {foo}' | ||
* ``` | ||
* | ||
* @param {Object} obj The object to iterate over | ||
* @param {Function(*, String)} callback The callback to call on each enumerable property value | ||
* @param {[Object]} context an optional `this` context for calling `callback` | ||
* @return {Array} the enumerable keys of `obj` as an Array | ||
*/ | ||
eachKey: function(obj, callback, context){ | ||
// each key in something map like | ||
// eachOwnEnumerableKey | ||
var enumerableKeys = this.getOwnEnumerableKeys(obj); | ||
@@ -66,5 +165,30 @@ return this.eachIndex(enumerableKeys, function(key){ | ||
}, | ||
// if a key or index | ||
// like has own property | ||
/** | ||
* @function can-reflect/shape.hasOwnKey hasOwnKey | ||
* @parent can-reflect/shape | ||
* @description Determine whether an object contains a key on itself, not only on its prototype chain | ||
* | ||
* @signature `hasOwnKey(obj, key)` | ||
* | ||
* Return `true` if an object's own properties include the property key `key`, `false` otherwise. | ||
* An object may implement [can-symbol/symbols/hasOwnKey @@@@can.hasOWnKey] to override default behavior. | ||
* By default, `canReflect.hasOwnKey` will first look for | ||
* [can-symbol/symbols/getOwnKey @@@@can.getOwnKey] on `obj`. If present, it will call `@@@@can.getOwnKey` and | ||
* test `key` against the returned Array of keys. If absent, `Object.prototype.hasOwnKey()` is used. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ "bar": "baz" }); | ||
* | ||
* canReflect.hasOwnKey(foo, "bar"); // -> true | ||
* canReflect.hasOwnKey(foo, "each"); // -> false | ||
* foo.each // -> function each() {...} | ||
* ``` | ||
* | ||
* @param {Object} obj Any MapLike object | ||
* @param {String} key The key to look up on `obj` | ||
* @return {Boolean} `true` if `obj`'s key set contains `key`, `false` otherwise | ||
*/ | ||
"hasOwnKey": function(obj, key){ | ||
// if a key or index | ||
// like has own property | ||
var hasOwnKey = obj[canSymbol.for("can.hasOwnKey")]; | ||
@@ -87,5 +211,36 @@ if(hasOwnKey) { | ||
}, | ||
// own enumerable keys (aliased as keys) | ||
/** | ||
* @function can-reflect/shape.getOwnEnumerableKeys getOwnEnumerableKeys | ||
* @parent can-reflect/shape | ||
* @description Return the list of keys which can be iterated over on an object | ||
* | ||
* @signature `getOwnEnumerableKeys(obj)` | ||
* | ||
* Return all keys on `obj` which have been defined as enumerable, either from explicitly setting | ||
* `enumerable` on the property descriptor, or by using `=` to set the value of the property without | ||
* a key descriptor, but excluding properties that only exist on `obj`'s prototype chaing. The | ||
* default behavior can be overridden by implementing | ||
* [can-symbol/symbols/getOwnEnumerableKeys @@@@can.getOwnEnumerableKeys] on `obj`. By default, | ||
* `canReflect.getOwnEnumerableKeys` will use [can-symbol/symbols/getOwnKeys @@@@can.getOwnKeys] to | ||
* retrieve the set of keys and [can-symbol/symbols/getOwnKeyDescriptor @@@@can.getOwnKeyDescriptor] | ||
* to filter for those which are enumerable. If either symbol is absent from `obj`, `Object.keys` | ||
* is used. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ bar: "baz", [canSymbol.for("quux")]: "thud" }); | ||
* Object.defineProperty(foo, "jeek", { | ||
* enumerable: true, | ||
* value: "plonk" | ||
* }); | ||
* | ||
* canReflect.getOwnEnumerableKeys(foo); // -> ["bar", "jeek"] | ||
* ``` | ||
* | ||
* @param {Object} obj Any Map-like object | ||
* @return {Array} the Array of all enumerable keys from the object, either using | ||
* [can-symbol/symbols/getOwnEnumerableKeys `@@@@can.getOwnEnumerableKeys`] from `obj`, or filtering | ||
* `obj`'s own keys for those which are enumerable. | ||
*/ | ||
getOwnEnumerableKeys: function(obj){ | ||
// own enumerable keys (aliased as keys) | ||
var getOwnEnumerableKeys = obj[canSymbol.for("can.getOwnEnumerableKeys")]; | ||
@@ -125,4 +280,28 @@ if(getOwnEnumerableKeys) { | ||
}, | ||
// own enumerable&non-enumerable keys (Object.getOwnPropertyNames) | ||
/** | ||
* @function can-reflect/shape.getOwnKeys getOwnKeys | ||
* @parent can-reflect/shape | ||
* @description Return the list of keys on an object, whether or not they can be iterated over | ||
* | ||
* @signature `getOwnKeys(obj)` | ||
* | ||
* Return the Array of all String (not Symbol) keys from `obj`, whether they are enumerable or not. If | ||
* [can-symbol/symbols/getOwnKeys @@@@can.getOwnKeys] exists on `obj`, it is called to return | ||
* the keys; otherwise, `Object.getOwnPropertyNames()` is used. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ bar: "baz", [canSymbol.for("quux")]: "thud" }); | ||
* Object.defineProperty(foo, "jeek", { | ||
* enumerable: false, | ||
* value: "plonk" | ||
* }); | ||
* | ||
* canReflect.getOwnKeys(foo); // -> ["bar", "jeek"] | ||
* ``` | ||
* | ||
* @param {Object} obj Any MapLike object | ||
* @return {Array} the Array of all String keys from the object. | ||
*/ | ||
getOwnKeys: function(obj){ | ||
// own enumerable&non-enumerable keys (Object.getOwnPropertyNames) | ||
var getOwnKeys = obj[canSymbol.for("can.getOwnKeys")]; | ||
@@ -135,2 +314,26 @@ if(getOwnKeys) { | ||
}, | ||
/** | ||
* @function can-reflect/shape.getOwnKeyDescriptor getOwnKeyDescriptor | ||
* @parent can-reflect/shape | ||
* @description Return a property descriptor for a named property on an object. | ||
* | ||
* @signature `getOwnKeyDescriptor(obj, key)` | ||
* | ||
* Return the key descriptor for the property key `key` on the Map-like object `obj`. A key descriptor | ||
* is specified in ECMAScript 5 and contains keys for the property's `configurable` and `enumerable` states, | ||
* as well as either `value` and `writable` for value properties, or `get` and `set` for getter/setter properties. | ||
* | ||
* The default behavior can be overridden by implementing [can-symbol/symbols/getOwnKeyDescriptor @@@@can.getOwnKeyDescriptor] | ||
* on `obj`; otherwise the default is to call `Object.getOwnKeyDescriptor()`. | ||
* | ||
* ``` | ||
* var foo = new DefineMap({ bar: "baz" }); | ||
* | ||
* getOwnKeyDescriptor(foo, "bar"); // -> {configurable: true, writable: true, enumerable: true, value: "baz"} | ||
* ``` | ||
* | ||
* @param {Object} obj Any object with named properties | ||
* @param {String} key The property name to look up on `obj` | ||
* @return {Object} A key descriptor object | ||
*/ | ||
getOwnKeyDescriptor: function(obj, key){ | ||
@@ -137,0 +340,0 @@ var getOwnKeyDescriptor = obj[canSymbol.for("can.getOwnKeyDescriptor")]; |
@@ -25,4 +25,17 @@ var QUnit = require('steal-qunit'); | ||
QUnit.test("isFunctionLike", function(){ | ||
ok(!typeReflections.isFunctionLike({})); | ||
ok(typeReflections.isFunctionLike(function(){})); | ||
ok(!typeReflections.isFunctionLike({}), 'object is not function like'); | ||
ok(typeReflections.isFunctionLike(function(){}), 'function is function like'); | ||
var nonFunctionFunction = function() {}; | ||
getSetReflections.setKeyValue(nonFunctionFunction, canSymbol.for("can.isFunctionLike"), false); | ||
ok(!typeReflections.isFunctionLike(nonFunctionFunction), 'function with can.isFunctionLike set to false is not function like'); | ||
var obj = {}; | ||
var func = function() {}; | ||
getSetReflections.setKeyValue(obj, canSymbol.for("can.new"), func); | ||
getSetReflections.setKeyValue(obj, canSymbol.for("can.apply"), func); | ||
ok(typeReflections.isFunctionLike(obj), 'object with can.new and can.apply symbols is function like'); | ||
getSetReflections.setKeyValue(obj, canSymbol.for("can.isFunctionLike"), false); | ||
ok(!typeReflections.isFunctionLike(obj), 'object with can.new, can.apply, and can.isFunctionLike set to false is not function like'); | ||
}); | ||
@@ -29,0 +42,0 @@ |
@@ -12,2 +12,25 @@ var canSymbol = require("can-symbol"); | ||
/** | ||
* @function can-reflect/type.isConstructorLike isConstructorLike | ||
* @parent can-reflect/type | ||
* | ||
* @signature `isConstructorLike(func)` | ||
* | ||
* Return `true` if `func` is a function and has a non-empty prototype, or implements | ||
* [can-symbol/symbols/new `@@@@can.new`]; `false` otherwise. | ||
* | ||
* ``` | ||
* canReflect.isConstructorLike(function() {}); // -> false | ||
* | ||
* function Construct() {} | ||
* Construct.prototype = { foo: "bar" }; | ||
* canReflect.isConstructorLike(Construct); // -> true | ||
* | ||
* canReflect.isConstructorLike({}); // -> false | ||
* !!canReflect.isConstructorLike({ [canSymbol.for("can.new")]: function() {} }); // -> true | ||
* ``` | ||
* | ||
* @param {*} func maybe a function | ||
* @return {Boolean} | ||
*/ | ||
function isConstructorLike(func){ | ||
@@ -31,10 +54,57 @@ /* jshint unused: false */ | ||
/** | ||
* @function can-reflect/type.isFunctionLike isFunctionLike | ||
* @parent can-reflect/type | ||
* @signature `isFunctionLike(obj)` | ||
* | ||
* Return `true` if `func` is a function, or implements | ||
* [can-symbol/symbols/new `@@@@can.new`] or [can-symbol/symbols/apply `@@@@can.apply`]; `false` otherwise. | ||
* | ||
* ``` | ||
* canReflect.isFunctionLike(function() {}); // -> true | ||
* canReflect.isFunctionLike({}); // -> false | ||
* canReflect.isFunctionLike({ [canSymbol.for("can.apply")]: function() {} }); // -> true | ||
* ``` | ||
* | ||
* @param {*} obj maybe a function | ||
* @return {Boolean} | ||
*/ | ||
function isFunctionLike(obj){ | ||
var result = check(["can.new","can.apply"], obj); | ||
var result, | ||
symbolValue = obj[canSymbol.for("can.isFunctionLike")]; | ||
if (symbolValue !== undefined) { | ||
return symbolValue; | ||
} | ||
result = check(["can.new","can.apply"], obj); | ||
if(result !== undefined) { | ||
return !!result; | ||
} | ||
return typeof obj === "function"; | ||
} | ||
/** | ||
* @function can-reflect/type.isPrimitive isPrimitive | ||
* @parent can-reflect/type | ||
* | ||
* @signature `isPrimitive(obj)` | ||
* | ||
* Return `true` if `obj` is not a function nor an object via `typeof`, or is null; `false` otherwise. | ||
* | ||
* ``` | ||
* canReflect.isPrimitive(null); // -> true | ||
* canReflect.isPrimitive({}); // -> false | ||
* canReflect.isPrimitive(undefined); // -> true | ||
* canReflect.isPrimitive(1); // -> true | ||
* canReflect.isPrimitive([]); // -> false | ||
* canReflect.isPrimitive(function() {}); // -> false | ||
* canReflect.isPrimitive("foo"); // -> true | ||
* | ||
* ``` | ||
* | ||
* @param {*} obj maybe a primitive value | ||
* @return {Boolean} | ||
*/ | ||
function isPrimitive(obj){ | ||
@@ -49,2 +119,25 @@ var type = typeof obj; | ||
/** | ||
* @function can-reflect/type.isValueLike isValueLike | ||
* @parent can-reflect/type | ||
* | ||
* @signature `isValueLike(obj)` | ||
* | ||
* Return `true` if `obj` is a primitive or implements [can-symbol/symbols/getValue `@@can.getValue`], | ||
* `false` otherwise. | ||
* | ||
* ``` | ||
* canReflect.isValueLike(null); // -> true | ||
* canReflect.isValueLike({}); // -> false | ||
* canReflect.isValueLike(function() {}); // -> false | ||
* canReflect.isValueLike({ [canSymbol.for("can.isValueLike")]: true}); // -> true | ||
* canReflect.isValueLike({ [canSymbol.for("can.getValue")]: function() {} }); // -> true | ||
* canReflect.isValueLike(canCompute()); // -> true | ||
* canReflect.isValueLike(new DefineMap()); // -> false | ||
* | ||
* ``` | ||
* | ||
* @param {*} obj maybe a primitive or an object that yields a value | ||
* @return {Boolean} | ||
*/ | ||
function isValueLike(obj) { | ||
@@ -65,10 +158,36 @@ var symbolValue; | ||
/** | ||
* @function can-reflect/type.isMapLike isMapLike | ||
* @parent can-reflect/type | ||
* | ||
* @signature `isMapLike(obj)` | ||
* | ||
* Return `true` if `obj` is _not_ a primitive, does _not_ have a falsy value for | ||
* [can-symbol/symbols/isMapLike `@@@@can.isMapLike`], or alternately implements | ||
* [can-symbol/symbols/getKeyValue `@@@@can.getKeyValue`]; `false` otherwise. | ||
* | ||
* ``` | ||
* canReflect.isMapLike(null); // -> false | ||
* canReflect.isMapLike(1); // -> false | ||
* canReflect.isMapLike("foo"); // -> false | ||
* canReflect.isMapLike({}); // -> true | ||
* canReflect.isMapLike(function() {}); // -> true | ||
* canReflect.isMapLike([]); // -> false | ||
* canReflect.isMapLike({ [canSymbol.for("can.isMapLike")]: false }); // -> false | ||
* canReflect.isMapLike({ [canSymbol.for("can.getKeyValue")]: null }); // -> false | ||
* canReflect.isMapLike(canCompute()); // -> false | ||
* canReflect.isMapLike(new DefineMap()); // -> true | ||
* | ||
* ``` | ||
* | ||
* @param {*} obj maybe a Map-like | ||
* @return {Boolean} | ||
*/ | ||
function isMapLike(obj) { | ||
var symbolValue; | ||
if(isPrimitive(obj)) { | ||
return false; | ||
} | ||
symbolValue = obj[canSymbol.for("can.isMapLike")]; | ||
if( typeof symbolValue !== "undefined") { | ||
return symbolValue; | ||
var isMapLike = obj[canSymbol.for("can.isMapLike")]; | ||
if(typeof isMapLike !== "undefined") { | ||
return !!isMapLike; | ||
} | ||
@@ -83,2 +202,27 @@ var value = obj[canSymbol.for("can.getKeyValue")]; | ||
/** | ||
* @function can-reflect/type.isObservableLike isObservableLike | ||
* @parent can-reflect/type | ||
* | ||
* @signature `isObservableLike(obj)` | ||
* | ||
* Return `true` if `obj` is _not_ a primitive and implements any of | ||
* [can-symbol/symbols/onValue `@@@@can.onValue`], [can-symbol/symbols/onKeyValue `@@@@can.onKeyValue`], | ||
* [can-symbol/symbols/onKeys `@@@@can.onKeys`], | ||
* or [can-symbol/symbols/onKeysAdded `@@@@can.onKeysAdded`]; `false` otherwise. | ||
* | ||
* ``` | ||
* canReflect.isObservableLike(null); // -> false | ||
* canReflect.isObservableLike({}); // -> false | ||
* canReflect.isObservableLike([]); // -> false | ||
* canReflect.isObservableLike(function() {}); // -> false | ||
* canReflect.isObservableLike({ [canSymbol.for("can.onValue")]: function() {} }); // -> true | ||
* canReflect.isObservableLike({ [canSymbol.for("can.onKeyValue")]: function() {} }); // -> true | ||
* canReflect.isObservableLike(canCompute())); // -> true | ||
* canReflect.isObservableLike(new DefineMap())); // -> true | ||
* ``` | ||
* | ||
* @param {*} obj maybe an observable | ||
* @return {Boolean} | ||
*/ | ||
function isObservableLike( obj ) { | ||
@@ -94,2 +238,30 @@ if(isPrimitive(obj)) { | ||
/** | ||
* @function can-reflect/type.isListLike isListLike | ||
* @parent can-reflect/type | ||
* | ||
* @signature `isListLike(list)` | ||
* | ||
* Return `true` if `list` is a `String`, <br>OR `list` is _not_ a primitive and implements `@@@@iterator`, | ||
* <br>OR `list` is _not_ a primitive and returns `true` for `Array.isArray()`, <br>OR `list` is _not_ a primitive and has a | ||
* numerical length and is either empty (`length === 0`) or has a last element at index `length - 1`; <br>`false` otherwise | ||
* | ||
* ``` | ||
* canReflect.isListLike(null); // -> false | ||
* canReflect.isListLike({}); // -> false | ||
* canReflect.isListLike([]); // -> true | ||
* canReflect.isListLike("foo"); // -> true | ||
* canReflect.isListLike(1); // -> false | ||
* canReflect.isListLike({ [canSymbol.for("can.isListLike")]: true }); // -> true | ||
* canReflect.isListLike({ [canSymbol.iterator]: function() {} }); // -> true | ||
* canReflect.isListLike({ length: 0 }); // -> true | ||
* canReflect.isListLike({ length: 3 }); // -> false | ||
* canReflect.isListLike({ length: 3, "2": true }); // -> true | ||
* canReflect.isListLike(new DefineMap()); // -> false | ||
* canReflect.isListLike(new DefineList()); // -> true | ||
* ``` | ||
* | ||
* @param {*} list maybe a List-like | ||
* @return {Boolean} | ||
*/ | ||
function isListLike( list ) { | ||
@@ -126,2 +298,26 @@ var symbolValue, | ||
} | ||
/** | ||
* @function can-reflect/type.isSymbolLike isSymbolLike | ||
* @parent can-reflect/type | ||
* | ||
* @signature `isSymbolLike(symbol)` | ||
* | ||
* Return `true` if `symbol` is a native Symbol, or evaluates to a String with a prefix | ||
* equal to that of CanJS's symbol polyfill; `false` otherwise. | ||
* | ||
* ``` | ||
* /* ES6 *\/ canReflect.isSymbolLike(Symbol.iterator); // -> true | ||
* canReflect.isSymbolLike(canSymbol.for("foo")); // -> true | ||
* canReflect.isSymbolLike("@@symbol.can.isSymbol"); // -> true (due to polyfill for non-ES6) | ||
* canReflect.isSymbolLike("foo"); // -> false | ||
* canReflect.isSymbolLike(null); // -> false | ||
* canReflect.isSymbolLike(1); // -> false | ||
* canReflect.isSymbolLike({}); // -> false | ||
* canReflect.isSymbolLike({ toString: function() { return "@@symbol.can.isSymbol"; } }); // -> true | ||
* ``` | ||
* | ||
* @param {*} symbol maybe a symbol | ||
* @return {Boolean} | ||
*/ | ||
var symbolStart = "@@symbol"; | ||
@@ -136,2 +332,9 @@ function isSymbolLike( symbol ) { | ||
/** | ||
* @module can-reflect/type Type | ||
* @parent can-reflect | ||
* | ||
* The `type` module deals with how to determine if a given object matches any of the familiar types to CanJS: | ||
* constructors, functions, lists, maps, observables (which are also lists and maps), primitives, values, and symbols. | ||
*/ | ||
module.exports = { | ||
@@ -146,2 +349,23 @@ isConstructorLike: isConstructorLike, | ||
isSymbolLike: isSymbolLike, | ||
/** | ||
* @function can-reflect/type.isMoreListLikeThanMapLike isMoreListLikeThanMapLike | ||
* @parent can-reflect/type | ||
* | ||
* @signature `isMoreListLikeThanMapLike(obj)` | ||
* | ||
* Return `true` if `obj` is an Array, declares itself to be more ListLike with | ||
* `@@@@can.isMoreListLikeThanMapLike`, or self-reports as ListLike but not as MapLike; `false` otherwise. | ||
* | ||
* ``` | ||
* canReflect.isMoreListLikeThanMapLike([]); // -> true | ||
* canReflect.isMoreListLikeThanMapLike(null); // -> undefined | ||
* canReflect.isMoreListLikeThanMapLike({}); // -> false | ||
* canReflect.isMoreListLikeThanMapLike(new DefineList()); // -> true | ||
* canReflect.isMoreListLikeThanMapLike(new DefineMap()); // -> false | ||
* canReflect.isMoreListLikeThanMapLike(function() {}); // -> false | ||
* ``` | ||
* | ||
* @param {Object} obj the object to test for ListLike against MapLike traits. | ||
* @return {Boolean} | ||
*/ | ||
isMoreListLikeThanMapLike: function(obj){ | ||
@@ -163,2 +387,22 @@ if(Array.isArray(obj)) { | ||
}, | ||
/** | ||
* @function can-reflect/type.isIteratorLike isIteratorLike | ||
* @parent can-reflect/type | ||
* | ||
* @signature `isIteratorLike(obj)` | ||
* | ||
* Return `true` if `obj` has a key `"next"` pointing to a zero-argument function; `false` otherwise | ||
* | ||
* ``` | ||
* canReflect.isIteratorLike([][Symbol.iterator]()); // -> true | ||
* canReflect.isIteratorLike(new DefineList()[canSymbol.iterator]()); // -> true | ||
* canReflect.isIteratorLike(new DefineMap()[canSymbol.iterator]()); // -> true | ||
* canReflect.isIteratorLike(null); // -> false | ||
* canReflect.isIteratorLike({ next: function() {} }); // -> true | ||
* canReflect.isIteratorLike({ next: function(foo) {} }); // -> false (iterator nexts do not take arguments) | ||
* ``` | ||
* | ||
* @param {Object} obj the object to test for Iterator traits | ||
* @return {Boolean} | ||
*/ | ||
isIteratorLike: function(obj){ | ||
@@ -165,0 +409,0 @@ return obj && |
83384
2039