From dfa7119a5410853719c8a66df324cc1384f9e3f2 Mon Sep 17 00:00:00 2001 From: dsc Date: Thu, 29 Mar 2012 01:51:47 -0700 Subject: [PATCH] kraken/{ -> util/}underscore --- lib/base.co | 2 +- lib/chart/chart-library.co | 2 +- lib/chart/dygraphs-library.co | 2 +- lib/graph/graph-model.co | 2 +- lib/graph/graph-view.co | 2 +- lib/scaffold/scaffold-model.co | 2 +- lib/scaffold/scaffold-view.co | 2 +- lib/underscore/array.co | 48 -------- lib/underscore/functions.co | 220 -------------------------------------- lib/underscore/index.co | 24 ---- lib/underscore/kv.co | 60 ---------- lib/underscore/object.co | 192 --------------------------------- lib/underscore/string.co | 58 ---------- lib/util/aliasdict.co | 2 +- lib/util/cascade.co | 2 +- lib/util/index.co | 11 +- lib/util/parser.co | 2 +- lib/util/underscore/array.co | 48 ++++++++ lib/util/underscore/functions.co | 220 ++++++++++++++++++++++++++++++++++++++ lib/util/underscore/index.co | 24 ++++ lib/util/underscore/kv.co | 60 ++++++++++ lib/util/underscore/object.co | 192 +++++++++++++++++++++++++++++++++ lib/util/underscore/string.co | 58 ++++++++++ lib/vis/vis-model.co | 2 +- lib/vis/vis-view.co | 2 +- test/util/cascade-test.co | 2 +- www/misc/numbers.co | 2 +- www/modules.yaml | 12 +- 28 files changed, 627 insertions(+), 628 deletions(-) delete mode 100644 lib/underscore/array.co delete mode 100644 lib/underscore/functions.co delete mode 100644 lib/underscore/index.co delete mode 100644 lib/underscore/kv.co delete mode 100644 lib/underscore/object.co delete mode 100644 lib/underscore/string.co create mode 100644 lib/util/underscore/array.co create mode 100644 lib/util/underscore/functions.co create mode 100644 lib/util/underscore/index.co create mode 100644 lib/util/underscore/kv.co create mode 100644 lib/util/underscore/object.co create mode 100644 lib/util/underscore/string.co diff --git a/lib/base.co b/lib/base.co index f1e6235..4d34594 100644 --- a/lib/base.co +++ b/lib/base.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' op = require 'kraken/util/op' Backbone = require 'backbone' diff --git a/lib/chart/chart-library.co b/lib/chart/chart-library.co index dee4ece..75c7a13 100644 --- a/lib/chart/chart-library.co +++ b/lib/chart/chart-library.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' op = require 'kraken/util/op' {EventEmitter} = require 'events' {Parsers, ParserMixin} = require 'kraken/util/parser' diff --git a/lib/chart/dygraphs-library.co b/lib/chart/dygraphs-library.co index 2487107..f08468d 100644 --- a/lib/chart/dygraphs-library.co +++ b/lib/chart/dygraphs-library.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' { ChartLibrary, ChartOption, } = require 'kraken/chart/chart-library' diff --git a/lib/graph/graph-model.co b/lib/graph/graph-model.co index 5df59fd..39e1109 100644 --- a/lib/graph/graph-model.co +++ b/lib/graph/graph-model.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' { BaseModel, BaseView, } = require 'kraken/base' { Field, FieldList, FieldView, Scaffold, diff --git a/lib/graph/graph-view.co b/lib/graph/graph-view.co index 0cb3982..ad07954 100644 --- a/lib/graph/graph-view.co +++ b/lib/graph/graph-view.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' {BaseView} = require 'kraken/base' { Field, FieldList, FieldView, Scaffold, } = require 'kraken/scaffold' diff --git a/lib/scaffold/scaffold-model.co b/lib/scaffold/scaffold-model.co index 9d242a6..eda72ed 100644 --- a/lib/scaffold/scaffold-model.co +++ b/lib/scaffold/scaffold-model.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' op = require 'kraken/util/op' { BaseModel, BaseList, } = require 'kraken/base' diff --git a/lib/scaffold/scaffold-view.co b/lib/scaffold/scaffold-view.co index a766afb..21aa194 100644 --- a/lib/scaffold/scaffold-view.co +++ b/lib/scaffold/scaffold-view.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' op = require 'kraken/util/op' { BaseView, } = require 'kraken/base' diff --git a/lib/underscore/array.co b/lib/underscore/array.co deleted file mode 100644 index 76e078a..0000000 --- a/lib/underscore/array.co +++ /dev/null @@ -1,48 +0,0 @@ -_ = require 'underscore' - -I = -> it -defined = (o) -> o? - -_array = do - /** - * Transforms an Array of tuples (two-element Arrays) into an Object, such that for each - * tuple [k, v]: - * result[k] = v if filter(v) - * @param {Array} o A collection. - * @param {Function} [filter=defined] Optional filter function. If omitted, will - * exclude `undefined` and `null` values. - * @return {Object} Transformed result. - */ - generate : (o, filter=defined) -> - _.reduce do - o - (acc, [k, v], idx) -> - if k and (not filter or filter(v, k)) - acc[k] = v - acc - {} - - /** - * As {@link _.generate}, but first transforms the collection using `fn`. - * @param {Array} o A collection. - * @param {Function} [fn=I] Transformation function. Defaults to the identity transform. - * @param {Function} [filter=defined] Optional filter function. If omitted, will - * exclude `undefined` and `null` values. - * @param {Object} [context=o] Function context. - * @return {Object} Transformed result. - */ - synthesize : (o, fn=I, filter=defined, context) -> - _array.generate _.map(o, fn, context), filter - - - /** - * Symmetric Difference - */ - xor : (a, b) -> - a = _.values a - b = _.values b - return _.union _.difference(a,b), _.difference(b,a) - - - -exports import _array diff --git a/lib/underscore/functions.co b/lib/underscore/functions.co deleted file mode 100644 index 7327961..0000000 --- a/lib/underscore/functions.co +++ /dev/null @@ -1,220 +0,0 @@ -_ = require 'underscore' - - -slice = [].slice -hasOwn = {}.hasOwnProperty -objToString = {}.toString - -toArray = _.toArray - - - -decorate = (fn) -> - if not fn.__decorated__ - for name of _pet.FUNCTION_METHODS - m = _[name] - fn[name] = m.__methodized__ or methodize m - fn.__decorated__ = true - return fn - -methodize = (fn) -> - m = fn.__methodized__ - return m if m - - g = fn.__genericized__ - return g.__wraps__ if g and g.__wraps__ - - m = fn.__methodized__ = (args...) -> - args.unshift this - return fn.apply this, args - - m.__wraps__ = fn - return decorate m - - - -_pet = module.exports = \ - function pet (o, start=0, end=undefined) -> - if _.isArguments o - o = _.toArray o, start, end - - return decorate o if typeof o is 'function' - return _ o - -# function methods to be attached on call to _(fn) -_pet.FUNCTION_METHODS = [ - 'bind', 'bindAll', 'memoize', - 'delay', 'defer', 'throttle', 'debounce', 'once', 'after', - 'wrap', 'compose', - 'unwrap', 'partial', 'curry', 'flip', 'methodize', 'aritize', 'limit' -] - - -class2name = "Boolean Number String Function Array Date RegExp Object" - .split(" ") - .reduce ((class2name, name) -> - class2name[ "[object "+name+"]" ] = name - return class2name), {} - - -## Objects -_.mixin - - has: (o, v) -> - vals = if _.isArray(o) then o else _.values(o) - return vals.indexOf(v) is not -1 - - remove: (o, vs...) -> - if _.isArray(o) - _.each vs, (v) -> - idx = o.indexOf v - if idx is not -1 - o.splice idx, 1 - else - _.each o, (v, k) -> - if vs.indexOf(v) != -1 - delete o[k] - return o - - set: (o, key, value, def) -> - if o and key? and (value? or def?) - o[key] = value ? def - return o - - attr: (o, key, value, def) -> - return o if not o or key is undefined - - if _.isPlainObject key - return _.extend o, key - - if (value ? def) is not undefined - return _.set o, key, value, def - - return o[key] - - - -## Types -_.mixin - - basicTypeName: (o) -> - return if o is null then "null" else (class2name[objToString.call(o)] || "Object") - - isWindow: (o) -> - return o and typeof o is "object" and "setInterval" of o - - isPlainObject: (o) -> - # Must be an Object. - # Because of IE, we also have to check the presence of the constructor property. - # Make sure that DOM nodes and window objects don't pass through, as well - if not o or basicTypeName(o) is not "Object" or o.nodeType or _.isWindow(o) - return false - - # Not own constructor property? must be Object - C = o.constructor - if C and not hasOwn.call(o, "constructor") and not hasOwn.call(C.prototype, "isPrototypeOf") - return false - - # Own properties are enumerated firstly, so to speed up, - # if last one is own, then all properties are own. - for key in o - ; # semicolon **on new line** is required by coffeescript to denote empty statement. - - return key is void or hasOwn.call(o, key) - - -## Arrays -_.mixin - - toArray: (iterable, start=0, end=undefined) -> - _.slice toArray(iterable), start, end - - flatten: (A) -> - _.reduce do - slice.call(arguments) - (flat, v) -> - flat.concat( if _.isArray v then _.reduce(v, arguments.callee, []) else v ) - [] - - - -## Functions -_ofArity = _.memoize do - (n, limit) -> - args = ( '$'+i for i from 0 til n ).join(',') - name = ( if limit then 'limited' else 'artized' ) - apply_with = ( if limit then "[].slice.call(arguments, 0, #{n})" else 'arguments' ) - return eval " - (function #{name}(fn){ - var _fn = function(#{args}){ return fn.apply(this, #{apply_with}); }; - _fn.__wraps__ = fn; - return _(_fn); - })" - -_.mixin do - methodize - - unwrap: (fn) -> - (fn and _.isFunction(fn) and _.unwrap(fn.__wraps__)) or fn - - - partial: (fn, ...args) -> - partially = -> - fn.apply this, args.concat slice.call(arguments) - _ partially import { __wraps__:fn } - - - genericize: (fn) -> - return that if fn.__genericized__ - return that if fn.__methodized__?.__wraps__ - - fn.__genericized__ = (...args) -> - fn.apply args.shift(), args - - _ fn.__genericized__ import { __wraps__:fn } - - - curry: (fn, ...args) -> - return fn unless _.isFunction fn - return fn.apply this, args if fn.__curried__ - - L = fn.length or _.unwrap(fn).length - return fn.apply this, args if args.length >= L - - curried = -> - _args = args.concat slice.call(arguments) - return fn.apply this, _args if _args.length >= L - _args.unshift fn - _.curry.apply this, _args - - _ curried import - __wraps__ : fn - __curried__ : args - - - flip: (fn) -> - return that if fn.__flipped__ - - fn.__flipped__ = -> - [arguments[0], arguments[1]] = [arguments[1], arguments[0]] - fn ... - - _ fn.__flipped__ import { __wraps__:fn } - - - aritize: (fn, n) -> - return fn if fn.length is n - fn.__aritized__ or= {} - fn.__aritized__[n] or= _ofArity(n, false)(fn) - - - limit: (fn, n) -> - fn.__limited__ or= {} - fn.__limited__[n] or= _ofArity(n, true)(fn) - - - - - - -_.extend _pet, _ diff --git a/lib/underscore/index.co b/lib/underscore/index.co deleted file mode 100644 index ed3d85a..0000000 --- a/lib/underscore/index.co +++ /dev/null @@ -1,24 +0,0 @@ -_ = require 'underscore' -_.str = require 'underscore.string' -_.mixin _.str.exports() - -_.mixin require 'kraken/underscore/array' -_.mixin require 'kraken/underscore/object' -_.mixin require 'kraken/underscore/kv' -_.mixin require 'kraken/underscore/string' - - -## Debug -_.dump = (o, label='dump') -> - if not _.isArray(o) and _.isObject(o) - console.group label - for k, v in o - console.log "#k:", v - console.groupEnd() - else - console.log label, o - o - - -module.exports = exports = _ - diff --git a/lib/underscore/kv.co b/lib/underscore/kv.co deleted file mode 100644 index fdc31ba..0000000 --- a/lib/underscore/kv.co +++ /dev/null @@ -1,60 +0,0 @@ -_ = require 'underscore' - - -_kv = do - - /** - * Transforms an object to a string of URL-encoded KV-pairs (aka "www-form-encoding"). - */ - toKV: (o, item_delim='&', kv_delim='=') -> - _.reduce do - o - (acc, v, k) -> - acc.push encodeURIComponent(k)+kv_delim+encodeURIComponent(v) if k - acc - [] - .join item_delim - - /** - * Restores an object from a string of URL-encoded KV-pairs (aka "www-form-encoding"). - */ - fromKV: (qs, item_delim='&', kv_delim='=') -> - _.reduce do - qs.split item_delim - (acc, pair) -> - idx = pair.indexOf kv_delim - if idx is not -1 - [k, v] = [pair.slice(0, idx), pair.slice(idx+1)] - else - [k, v] = [pair, ''] - acc[ decodeURIComponent k ] = decodeURIComponent v if k - acc - {} - - /** - * Copies and flattens any sub-objects into namespaced keys on the parent object, such - * that `{ "foo":{ "bar":1 } }` becomes `{ "foo.bar":1 }`. - */ - collapseObject: (obj, parent={}, prefix='') -> - prefix += '.' if prefix - _.each obj, (v, k) -> - if _.isPlainObject v - _.collapseObject v, parent, prefix+k - else - parent[prefix+k] = v - parent - - /** - * Inverse of `.collapseObject()` -- copies and expands any dot-namespaced keys in the object, such - * that `{ "foo.bar":1 }` becomes `{ "foo":{ "bar":1 }}`. - */ - uncollapseObject: (obj) -> - _.reduce do - obj - (acc, v, k) -> - _.setNested acc, k, v, {+ensure} - acc - {} - - -exports import _kv diff --git a/lib/underscore/object.co b/lib/underscore/object.co deleted file mode 100644 index eb2ea2e..0000000 --- a/lib/underscore/object.co +++ /dev/null @@ -1,192 +0,0 @@ -_ = require 'underscore' - -OBJ_PROTO = Object.prototype -getProto = Object.getPrototypeOf - - -/** - * Default options for delegate-accessor functions. - */ -DEFAULT_DELEGATE_OPTIONS = - getter : 'get' - setter : 'set' - deleter : 'unset' - -/** - * Default options for nested-accessor functions. - */ -DEFAULT_NESTED_OPTIONS = {-ensure} import DEFAULT_DELEGATE_OPTIONS - -/** - * Sentinel for missing values. - */ -MISSING = {} - - -/** - * @namespace Functions for working with objects and object graphs. - */ -_obj = do - - /** - * @returns {Boolean} Whether value is a plain object or not. - */ - isPlainObject : (o) -> - !! o and _.isObject(o) and OBJ_PROTO is getProto(o) - - - /** - * In-place removal of a value from an Array or Object. - */ - remove: (obj, v) -> - values = [].slice.call arguments, 1 - if _.isArray obj - for v of values - idx = obj.indexOf v - obj.splice idx, 1 if idx is not -1 - else - for k, v in obj - delete obj[k] if -1 is not values.indexOf v - obj - - - /** - * Converts the collection to a list of its items: - * - Objects become a list of `[key, value]` pairs. - * - Strings become a list of characters. - * - Arguments objects become an array. - * - Arrays are copied. - */ - items: (obj) -> - if _.isObject(obj) and not _.isArguments(obj) - _.map obj, (v, k) -> [k, v] - else - [].slice.call obj - - - - ### Delegating Accessors - - get: (obj, key, def, opts) -> - return unless obj? - getter = opts?.getter or 'get' - if typeof obj[getter] is 'function' - obj[getter] key, def, opts - else - if obj[key] is not void then obj[key] else def - - set: (obj, key, value, opts) -> - return unless obj? - if _.isObject(key) and key? - [values, opts] = [key, value] - else - values = { "#key": value } - - setter = opts?.setter or 'set' - if typeof obj[setter] is 'function' - for key, value in values - obj[setter] key, value, opts - else - for key, value in values - obj[key] = value - - obj - - unset: (obj, key, opts) -> - return unless obj? - deleter = opts?.deleter or 'unset' - if typeof obj[deleter] is 'function' - obj[deleter] key, opts - else - delete obj[key] - - - - - - ### Nested Acccessors - - /** - * Searches a heirarchical object for a given subkey specified in dotted-property syntax, - * respecting sub-object accessor-methods (e.g., 'get', 'set') if they exist. - * - * @param {Object} base The object to serve as the root of the property-chain. - * @param {Array|String} chain The property-chain to lookup. - * @param {Object} [opts] Options: - * @param {Boolean} [opts.ensure=false] If true, intermediate keys that are `null` or - * `undefined` will be filled in with a new empty object `{}`, ensuring the get will - * return valid metadata. - * @param {String} [opts.getter="get"] Name of the sub-object getter method use if it exists. - * @param {String} [opts.setter="set"] Name of the sub-object setter method use if it exists. - * @param {String} [opts.deleter="unset"] Name of the sub-object deleter method use if it exists. - * @returns {undefined|Object} If found, the object is of the form - * `{ key: Qualified key name, obj: Parent object of key, val: Value at obj[key], opts: Options }`. - * Otherwise `undefined`. - */ - getNestedMeta : (obj, chain, opts) -> - chain = chain.split('.') if typeof chain is 'string' - len = chain.length - 1 - opts = _.clone(DEFAULT_NESTED_OPTIONS) import (opts or {}) - - _.reduce do - chain - (obj, key, idx, chain) -> - return void unless obj? - val = _.get obj, key, void, opts - if idx is len - return { key, val, obj, opts } - if not val? and opts.ensure - val = {} - _.set obj, key, val, opts - val - obj - - /** - * Searches a heirarchical object for a given subkey specified in dotted-property syntax. - * @param {Object} obj The object to serve as the root of the property-chain. - * @param {Array|String} chain The property-chain to lookup. - * @param {Any} [def=undefined] Value to return if lookup fails. - * @param {Object} [opts] Options to pass to `{@link #getNestedMeta}`. - * @returns {null|Object} If found, returns the value, and otherwise `default`. - */ - getNested : (obj, chain, def, opts) -> - {opts} = meta = _.getNestedMeta obj, chain, opts - return def if meta?.val is void - meta.val - - /** - * Searches a heirarchical object for a given subkey specified in - * dotted-property syntax, setting it with the provided value if found. - * @param {Object} obj The object to serve as the root of the property-chain. - * @param {Array|String} chain The property-chain to lookup. - * @param {Any} value The value to set. - * @param {Object} [opts] Options to pass to `{@link #getNestedMeta}`. - * @returns {undefined|Any} If found, returns the old value, and otherwise `undefined`. - */ - setNested : (obj, chain, value, opts) -> - {opts} = meta = _.getNestedMeta obj, chain, opts - return unless meta - _.set meta.obj, meta.key, value, opts - meta.val - - /** - * Searches a heirarchical object for a potentially-nested key and removes it. - * - * @param {Object} obj The root of the lookup chain. - * @param {String|Array} chain The chain of property-keys to navigate. - * Nested keys can be supplied as a dot-delimited string (e.g., `_.unsetNested(obj, 'user.name')`), - * or an array of strings, allowing for keys with dots (eg., - * `_.unsetNested(obj, ['products', 'by_price', '0.99'])`). - * @param {Object} [opts] Options to pass to `{@link #getNestedMeta}`. - * @returns {undefined|Any} The old value if found; otherwise `undefined`. - */ - unsetNested : (obj, chain, opts) -> - {opts} = meta = _.getNestedMeta obj, chain, opts - return unless meta - _.unset meta.obj, meta.key, opts - meta.val - - - - -exports import _obj diff --git a/lib/underscore/string.co b/lib/underscore/string.co deleted file mode 100644 index d83013e..0000000 --- a/lib/underscore/string.co +++ /dev/null @@ -1,58 +0,0 @@ -_ = require 'underscore' -_str = require 'underscore.string' - -_string = do - - drop : (s, ...parts) -> - do - starting = s - for part of parts - s .= slice part.length if _str.startsWith s, part - s .= slice 0, s.length-part.length if _str.endsWith s, part - while s and s is not starting - s - - ldrop : (s, ...parts) -> - do - starting = s - for part of parts - s .= slice part.length if _str.startsWith s, part - while s and s is not starting - s - - rdrop : (s, ...parts) -> - do - starting = s - for part of parts - s .= slice 0, s.length-part.length if _str.endsWith s, part - while s and s is not starting - s - - # Converts to snake_case, concatenates the key-value pair (with '_'), normalizing _'s. - # If only a key is given, domize auto-curries and waits for a second argument. - domize : (key='', value='') -> - key = _str.trim _str.underscored(key), '_' - if arguments.length <= 1 - arguments.callee.bind this, key - else - "#{key}_#{_str.trim _str.underscored(value), '_'}" - - shortname: (s) -> - return s if s.length <= 6 - parts = _ s - .chain() - .underscored() - .trim '_' - .value() - .replace /_+/g, '_' - .split '_' - .map -> _.capitalize it.slice 0, 2 - return s if parts.length is 1 #and s.length <= 8 - parts.shift().toLowerCase() + parts.join('') - - -_string import do - dropLeft : _string.ldrop - dropRight : _string.rdrop - -exports import _string diff --git a/lib/util/aliasdict.co b/lib/util/aliasdict.co index 81cb4ef..4df93b6 100644 --- a/lib/util/aliasdict.co +++ b/lib/util/aliasdict.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' /** * @class A mapping of key-value pairs supporting key-aliases. diff --git a/lib/util/cascade.co b/lib/util/cascade.co index f0c47e4..38847ff 100644 --- a/lib/util/cascade.co +++ b/lib/util/cascade.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' hasOwn = ({}).hasOwnProperty diff --git a/lib/util/index.co b/lib/util/index.co index 2cc39e1..caba6e5 100644 --- a/lib/util/index.co +++ b/lib/util/index.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' root = do -> this root.console or= _ <[ log info warn error dir table group groupCollapsed groupEnd ]> .synthesize -> [it, nop] @@ -10,11 +10,10 @@ root.jQuery?.fn.invoke = (method, ...args) -> op = require 'kraken/util/op' backbone = require 'kraken/util/backbone' +parser = require 'kraken/util/parser' +exports import { root, _, op, backbone, parser, } + # HashSet = require 'kraken/util/hashset' # BitString = require 'kraken/util/bitstring' # {crc32} = require 'kraken/util/crc' -parser = require 'kraken/util/parser' - - -exports import { root, _, op, backbone, parser, } -# exports import { root, _, op, HashSet, BitString, crc32, parser, } +# exports import { HashSet, BitString, crc32, } diff --git a/lib/util/parser.co b/lib/util/parser.co index e1f9744..abcb0ea 100644 --- a/lib/util/parser.co +++ b/lib/util/parser.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' op = require 'kraken/util/op' diff --git a/lib/util/underscore/array.co b/lib/util/underscore/array.co new file mode 100644 index 0000000..76e078a --- /dev/null +++ b/lib/util/underscore/array.co @@ -0,0 +1,48 @@ +_ = require 'underscore' + +I = -> it +defined = (o) -> o? + +_array = do + /** + * Transforms an Array of tuples (two-element Arrays) into an Object, such that for each + * tuple [k, v]: + * result[k] = v if filter(v) + * @param {Array} o A collection. + * @param {Function} [filter=defined] Optional filter function. If omitted, will + * exclude `undefined` and `null` values. + * @return {Object} Transformed result. + */ + generate : (o, filter=defined) -> + _.reduce do + o + (acc, [k, v], idx) -> + if k and (not filter or filter(v, k)) + acc[k] = v + acc + {} + + /** + * As {@link _.generate}, but first transforms the collection using `fn`. + * @param {Array} o A collection. + * @param {Function} [fn=I] Transformation function. Defaults to the identity transform. + * @param {Function} [filter=defined] Optional filter function. If omitted, will + * exclude `undefined` and `null` values. + * @param {Object} [context=o] Function context. + * @return {Object} Transformed result. + */ + synthesize : (o, fn=I, filter=defined, context) -> + _array.generate _.map(o, fn, context), filter + + + /** + * Symmetric Difference + */ + xor : (a, b) -> + a = _.values a + b = _.values b + return _.union _.difference(a,b), _.difference(b,a) + + + +exports import _array diff --git a/lib/util/underscore/functions.co b/lib/util/underscore/functions.co new file mode 100644 index 0000000..7327961 --- /dev/null +++ b/lib/util/underscore/functions.co @@ -0,0 +1,220 @@ +_ = require 'underscore' + + +slice = [].slice +hasOwn = {}.hasOwnProperty +objToString = {}.toString + +toArray = _.toArray + + + +decorate = (fn) -> + if not fn.__decorated__ + for name of _pet.FUNCTION_METHODS + m = _[name] + fn[name] = m.__methodized__ or methodize m + fn.__decorated__ = true + return fn + +methodize = (fn) -> + m = fn.__methodized__ + return m if m + + g = fn.__genericized__ + return g.__wraps__ if g and g.__wraps__ + + m = fn.__methodized__ = (args...) -> + args.unshift this + return fn.apply this, args + + m.__wraps__ = fn + return decorate m + + + +_pet = module.exports = \ + function pet (o, start=0, end=undefined) -> + if _.isArguments o + o = _.toArray o, start, end + + return decorate o if typeof o is 'function' + return _ o + +# function methods to be attached on call to _(fn) +_pet.FUNCTION_METHODS = [ + 'bind', 'bindAll', 'memoize', + 'delay', 'defer', 'throttle', 'debounce', 'once', 'after', + 'wrap', 'compose', + 'unwrap', 'partial', 'curry', 'flip', 'methodize', 'aritize', 'limit' +] + + +class2name = "Boolean Number String Function Array Date RegExp Object" + .split(" ") + .reduce ((class2name, name) -> + class2name[ "[object "+name+"]" ] = name + return class2name), {} + + +## Objects +_.mixin + + has: (o, v) -> + vals = if _.isArray(o) then o else _.values(o) + return vals.indexOf(v) is not -1 + + remove: (o, vs...) -> + if _.isArray(o) + _.each vs, (v) -> + idx = o.indexOf v + if idx is not -1 + o.splice idx, 1 + else + _.each o, (v, k) -> + if vs.indexOf(v) != -1 + delete o[k] + return o + + set: (o, key, value, def) -> + if o and key? and (value? or def?) + o[key] = value ? def + return o + + attr: (o, key, value, def) -> + return o if not o or key is undefined + + if _.isPlainObject key + return _.extend o, key + + if (value ? def) is not undefined + return _.set o, key, value, def + + return o[key] + + + +## Types +_.mixin + + basicTypeName: (o) -> + return if o is null then "null" else (class2name[objToString.call(o)] || "Object") + + isWindow: (o) -> + return o and typeof o is "object" and "setInterval" of o + + isPlainObject: (o) -> + # Must be an Object. + # Because of IE, we also have to check the presence of the constructor property. + # Make sure that DOM nodes and window objects don't pass through, as well + if not o or basicTypeName(o) is not "Object" or o.nodeType or _.isWindow(o) + return false + + # Not own constructor property? must be Object + C = o.constructor + if C and not hasOwn.call(o, "constructor") and not hasOwn.call(C.prototype, "isPrototypeOf") + return false + + # Own properties are enumerated firstly, so to speed up, + # if last one is own, then all properties are own. + for key in o + ; # semicolon **on new line** is required by coffeescript to denote empty statement. + + return key is void or hasOwn.call(o, key) + + +## Arrays +_.mixin + + toArray: (iterable, start=0, end=undefined) -> + _.slice toArray(iterable), start, end + + flatten: (A) -> + _.reduce do + slice.call(arguments) + (flat, v) -> + flat.concat( if _.isArray v then _.reduce(v, arguments.callee, []) else v ) + [] + + + +## Functions +_ofArity = _.memoize do + (n, limit) -> + args = ( '$'+i for i from 0 til n ).join(',') + name = ( if limit then 'limited' else 'artized' ) + apply_with = ( if limit then "[].slice.call(arguments, 0, #{n})" else 'arguments' ) + return eval " + (function #{name}(fn){ + var _fn = function(#{args}){ return fn.apply(this, #{apply_with}); }; + _fn.__wraps__ = fn; + return _(_fn); + })" + +_.mixin do + methodize + + unwrap: (fn) -> + (fn and _.isFunction(fn) and _.unwrap(fn.__wraps__)) or fn + + + partial: (fn, ...args) -> + partially = -> + fn.apply this, args.concat slice.call(arguments) + _ partially import { __wraps__:fn } + + + genericize: (fn) -> + return that if fn.__genericized__ + return that if fn.__methodized__?.__wraps__ + + fn.__genericized__ = (...args) -> + fn.apply args.shift(), args + + _ fn.__genericized__ import { __wraps__:fn } + + + curry: (fn, ...args) -> + return fn unless _.isFunction fn + return fn.apply this, args if fn.__curried__ + + L = fn.length or _.unwrap(fn).length + return fn.apply this, args if args.length >= L + + curried = -> + _args = args.concat slice.call(arguments) + return fn.apply this, _args if _args.length >= L + _args.unshift fn + _.curry.apply this, _args + + _ curried import + __wraps__ : fn + __curried__ : args + + + flip: (fn) -> + return that if fn.__flipped__ + + fn.__flipped__ = -> + [arguments[0], arguments[1]] = [arguments[1], arguments[0]] + fn ... + + _ fn.__flipped__ import { __wraps__:fn } + + + aritize: (fn, n) -> + return fn if fn.length is n + fn.__aritized__ or= {} + fn.__aritized__[n] or= _ofArity(n, false)(fn) + + + limit: (fn, n) -> + fn.__limited__ or= {} + fn.__limited__[n] or= _ofArity(n, true)(fn) + + + + + + +_.extend _pet, _ diff --git a/lib/util/underscore/index.co b/lib/util/underscore/index.co new file mode 100644 index 0000000..7e57eef --- /dev/null +++ b/lib/util/underscore/index.co @@ -0,0 +1,24 @@ +_ = require 'underscore' +_.str = require 'underscore.string' +_.mixin _.str.exports() + +_.mixin require 'kraken/util/underscore/array' +_.mixin require 'kraken/util/underscore/object' +_.mixin require 'kraken/util/underscore/kv' +_.mixin require 'kraken/util/underscore/string' + + +## Debug +_.dump = (o, label='dump') -> + if not _.isArray(o) and _.isObject(o) + console.group label + for k, v in o + console.log "#k:", v + console.groupEnd() + else + console.log label, o + o + + +module.exports = exports = _ + diff --git a/lib/util/underscore/kv.co b/lib/util/underscore/kv.co new file mode 100644 index 0000000..fdc31ba --- /dev/null +++ b/lib/util/underscore/kv.co @@ -0,0 +1,60 @@ +_ = require 'underscore' + + +_kv = do + + /** + * Transforms an object to a string of URL-encoded KV-pairs (aka "www-form-encoding"). + */ + toKV: (o, item_delim='&', kv_delim='=') -> + _.reduce do + o + (acc, v, k) -> + acc.push encodeURIComponent(k)+kv_delim+encodeURIComponent(v) if k + acc + [] + .join item_delim + + /** + * Restores an object from a string of URL-encoded KV-pairs (aka "www-form-encoding"). + */ + fromKV: (qs, item_delim='&', kv_delim='=') -> + _.reduce do + qs.split item_delim + (acc, pair) -> + idx = pair.indexOf kv_delim + if idx is not -1 + [k, v] = [pair.slice(0, idx), pair.slice(idx+1)] + else + [k, v] = [pair, ''] + acc[ decodeURIComponent k ] = decodeURIComponent v if k + acc + {} + + /** + * Copies and flattens any sub-objects into namespaced keys on the parent object, such + * that `{ "foo":{ "bar":1 } }` becomes `{ "foo.bar":1 }`. + */ + collapseObject: (obj, parent={}, prefix='') -> + prefix += '.' if prefix + _.each obj, (v, k) -> + if _.isPlainObject v + _.collapseObject v, parent, prefix+k + else + parent[prefix+k] = v + parent + + /** + * Inverse of `.collapseObject()` -- copies and expands any dot-namespaced keys in the object, such + * that `{ "foo.bar":1 }` becomes `{ "foo":{ "bar":1 }}`. + */ + uncollapseObject: (obj) -> + _.reduce do + obj + (acc, v, k) -> + _.setNested acc, k, v, {+ensure} + acc + {} + + +exports import _kv diff --git a/lib/util/underscore/object.co b/lib/util/underscore/object.co new file mode 100644 index 0000000..eb2ea2e --- /dev/null +++ b/lib/util/underscore/object.co @@ -0,0 +1,192 @@ +_ = require 'underscore' + +OBJ_PROTO = Object.prototype +getProto = Object.getPrototypeOf + + +/** + * Default options for delegate-accessor functions. + */ +DEFAULT_DELEGATE_OPTIONS = + getter : 'get' + setter : 'set' + deleter : 'unset' + +/** + * Default options for nested-accessor functions. + */ +DEFAULT_NESTED_OPTIONS = {-ensure} import DEFAULT_DELEGATE_OPTIONS + +/** + * Sentinel for missing values. + */ +MISSING = {} + + +/** + * @namespace Functions for working with objects and object graphs. + */ +_obj = do + + /** + * @returns {Boolean} Whether value is a plain object or not. + */ + isPlainObject : (o) -> + !! o and _.isObject(o) and OBJ_PROTO is getProto(o) + + + /** + * In-place removal of a value from an Array or Object. + */ + remove: (obj, v) -> + values = [].slice.call arguments, 1 + if _.isArray obj + for v of values + idx = obj.indexOf v + obj.splice idx, 1 if idx is not -1 + else + for k, v in obj + delete obj[k] if -1 is not values.indexOf v + obj + + + /** + * Converts the collection to a list of its items: + * - Objects become a list of `[key, value]` pairs. + * - Strings become a list of characters. + * - Arguments objects become an array. + * - Arrays are copied. + */ + items: (obj) -> + if _.isObject(obj) and not _.isArguments(obj) + _.map obj, (v, k) -> [k, v] + else + [].slice.call obj + + + + ### Delegating Accessors + + get: (obj, key, def, opts) -> + return unless obj? + getter = opts?.getter or 'get' + if typeof obj[getter] is 'function' + obj[getter] key, def, opts + else + if obj[key] is not void then obj[key] else def + + set: (obj, key, value, opts) -> + return unless obj? + if _.isObject(key) and key? + [values, opts] = [key, value] + else + values = { "#key": value } + + setter = opts?.setter or 'set' + if typeof obj[setter] is 'function' + for key, value in values + obj[setter] key, value, opts + else + for key, value in values + obj[key] = value + + obj + + unset: (obj, key, opts) -> + return unless obj? + deleter = opts?.deleter or 'unset' + if typeof obj[deleter] is 'function' + obj[deleter] key, opts + else + delete obj[key] + + + + + + ### Nested Acccessors + + /** + * Searches a heirarchical object for a given subkey specified in dotted-property syntax, + * respecting sub-object accessor-methods (e.g., 'get', 'set') if they exist. + * + * @param {Object} base The object to serve as the root of the property-chain. + * @param {Array|String} chain The property-chain to lookup. + * @param {Object} [opts] Options: + * @param {Boolean} [opts.ensure=false] If true, intermediate keys that are `null` or + * `undefined` will be filled in with a new empty object `{}`, ensuring the get will + * return valid metadata. + * @param {String} [opts.getter="get"] Name of the sub-object getter method use if it exists. + * @param {String} [opts.setter="set"] Name of the sub-object setter method use if it exists. + * @param {String} [opts.deleter="unset"] Name of the sub-object deleter method use if it exists. + * @returns {undefined|Object} If found, the object is of the form + * `{ key: Qualified key name, obj: Parent object of key, val: Value at obj[key], opts: Options }`. + * Otherwise `undefined`. + */ + getNestedMeta : (obj, chain, opts) -> + chain = chain.split('.') if typeof chain is 'string' + len = chain.length - 1 + opts = _.clone(DEFAULT_NESTED_OPTIONS) import (opts or {}) + + _.reduce do + chain + (obj, key, idx, chain) -> + return void unless obj? + val = _.get obj, key, void, opts + if idx is len + return { key, val, obj, opts } + if not val? and opts.ensure + val = {} + _.set obj, key, val, opts + val + obj + + /** + * Searches a heirarchical object for a given subkey specified in dotted-property syntax. + * @param {Object} obj The object to serve as the root of the property-chain. + * @param {Array|String} chain The property-chain to lookup. + * @param {Any} [def=undefined] Value to return if lookup fails. + * @param {Object} [opts] Options to pass to `{@link #getNestedMeta}`. + * @returns {null|Object} If found, returns the value, and otherwise `default`. + */ + getNested : (obj, chain, def, opts) -> + {opts} = meta = _.getNestedMeta obj, chain, opts + return def if meta?.val is void + meta.val + + /** + * Searches a heirarchical object for a given subkey specified in + * dotted-property syntax, setting it with the provided value if found. + * @param {Object} obj The object to serve as the root of the property-chain. + * @param {Array|String} chain The property-chain to lookup. + * @param {Any} value The value to set. + * @param {Object} [opts] Options to pass to `{@link #getNestedMeta}`. + * @returns {undefined|Any} If found, returns the old value, and otherwise `undefined`. + */ + setNested : (obj, chain, value, opts) -> + {opts} = meta = _.getNestedMeta obj, chain, opts + return unless meta + _.set meta.obj, meta.key, value, opts + meta.val + + /** + * Searches a heirarchical object for a potentially-nested key and removes it. + * + * @param {Object} obj The root of the lookup chain. + * @param {String|Array} chain The chain of property-keys to navigate. + * Nested keys can be supplied as a dot-delimited string (e.g., `_.unsetNested(obj, 'user.name')`), + * or an array of strings, allowing for keys with dots (eg., + * `_.unsetNested(obj, ['products', 'by_price', '0.99'])`). + * @param {Object} [opts] Options to pass to `{@link #getNestedMeta}`. + * @returns {undefined|Any} The old value if found; otherwise `undefined`. + */ + unsetNested : (obj, chain, opts) -> + {opts} = meta = _.getNestedMeta obj, chain, opts + return unless meta + _.unset meta.obj, meta.key, opts + meta.val + + + + +exports import _obj diff --git a/lib/util/underscore/string.co b/lib/util/underscore/string.co new file mode 100644 index 0000000..d83013e --- /dev/null +++ b/lib/util/underscore/string.co @@ -0,0 +1,58 @@ +_ = require 'underscore' +_str = require 'underscore.string' + +_string = do + + drop : (s, ...parts) -> + do + starting = s + for part of parts + s .= slice part.length if _str.startsWith s, part + s .= slice 0, s.length-part.length if _str.endsWith s, part + while s and s is not starting + s + + ldrop : (s, ...parts) -> + do + starting = s + for part of parts + s .= slice part.length if _str.startsWith s, part + while s and s is not starting + s + + rdrop : (s, ...parts) -> + do + starting = s + for part of parts + s .= slice 0, s.length-part.length if _str.endsWith s, part + while s and s is not starting + s + + # Converts to snake_case, concatenates the key-value pair (with '_'), normalizing _'s. + # If only a key is given, domize auto-curries and waits for a second argument. + domize : (key='', value='') -> + key = _str.trim _str.underscored(key), '_' + if arguments.length <= 1 + arguments.callee.bind this, key + else + "#{key}_#{_str.trim _str.underscored(value), '_'}" + + shortname: (s) -> + return s if s.length <= 6 + parts = _ s + .chain() + .underscored() + .trim '_' + .value() + .replace /_+/g, '_' + .split '_' + .map -> _.capitalize it.slice 0, 2 + return s if parts.length is 1 #and s.length <= 8 + parts.shift().toLowerCase() + parts.join('') + + +_string import do + dropLeft : _string.ldrop + dropRight : _string.rdrop + +exports import _string diff --git a/lib/vis/vis-model.co b/lib/vis/vis-model.co index 20929f1..1ec02e8 100644 --- a/lib/vis/vis-model.co +++ b/lib/vis/vis-model.co @@ -1,6 +1,6 @@ Seq = require 'seq' -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' Cascade = require 'kraken/util/cascade' { ChartLibrary, } = require 'kraken/chart' diff --git a/lib/vis/vis-view.co b/lib/vis/vis-view.co index 7ca9cec..40a7e89 100644 --- a/lib/vis/vis-view.co +++ b/lib/vis/vis-view.co @@ -1,6 +1,6 @@ root = do -> this -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' { BaseView, } = require 'kraken/base' { Field, FieldList, FieldView, Scaffold diff --git a/test/util/cascade-test.co b/test/util/cascade-test.co index 4524bf2..253d3c2 100644 --- a/test/util/cascade-test.co +++ b/test/util/cascade-test.co @@ -1,6 +1,6 @@ assert = require 'assert' -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' Cascade = require 'kraken/util/cascade' diff --git a/www/misc/numbers.co b/www/misc/numbers.co index 36bff8f..77c62a9 100644 --- a/www/misc/numbers.co +++ b/www/misc/numbers.co @@ -1,4 +1,4 @@ -_ = require 'kraken/underscore' +_ = require 'kraken/util/underscore' config = tbody = null diff --git a/www/modules.yaml b/www/modules.yaml index ce37523..0199df8 100644 --- a/www/modules.yaml +++ b/www/modules.yaml @@ -44,13 +44,13 @@ dev: paths: - js: - kraken: - - underscore: - - array - - object - - kv - - string - - index - util: + - underscore: + - array + - object + - kv + - string + - index - op - backbone - parser -- 1.7.0.4