New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

sugar

Package Overview
Dependencies
Maintainers
1
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sugar - npm Package Compare versions

Comparing version 0.9.4 to 0.9.5

unit_tests/environments/underscore/adapter.js

54

CHANGELOG.md

@@ -0,1 +1,55 @@

v0.9.5
=====
### API Changes ###
- .sugar method added to all classes to reinstate Sugar methods conditionally.
- Object.clone is now shallow by default, with an option for deep cloning
- Object.merge will now ignore non-objects
- Object.fromQueryString now takes the place of String#toObject.
- Nested object/array param parsing now possible with Object.fromQueryString.
- Array#remove now accepts unlimited parameters
- Array#exclude now accepts unlimited parameters
- Array#union now accepts unlimited parameters
- Array#subtract now accepts unlimited parameters
- Array#intersect now accepts unlimited parameters
- Array#split deprecated
- Array#compact no longer removes functions (bug)
- Array#compact now accepts flag to compact all falsy values
- Number#upto and Number#downto now accept a third parameter to traverse in multiples of > 1
- Number#pad now accepts a third parameter that is the base of the number
- Number#hex now accepts a parameter to pad the resulting string with a default of 1
- String#escapeHTML added
- String#truncate added. Will truncate a string without breaking words.
- String#toObject now refactored to Object.fromQueryString
- Function.lazy refactored to Function#lazy
- Function#lazy functions can now be cancelled via Function#cancel
- Function#defer deprecated -> use Function#delay instead
- Function#debounce added
- Function#after added
- Function#once added
### Internal Changes ###
- extendWithNativeCondition removed. Functionality now contained in extend
- shuffled and removed some dependencies to make it easier to extract the date module
- more robust equality comparison:
- multiArgs added to collect arguments
- array indexes now checked with hasProperty instead of hasOwnProperty
- object builders are now going through extend so they can store their references
- Object.clone refactored to use multiArgs
- Object.isEmpty now returns false if passed argument itself is falsy
- String#stripTags refactored to use multiArgs
- String#removeTags refactored to use multiArgs
-- "null" now taken into consideration for objects
-- object key length compared
-- strict equality matches in multiMatch
v0.9.4
=====
- Emergency fix for Array#compact incorrectly detecting NaN.
v0.9.3

@@ -2,0 +56,0 @@ =====

690

lib/analyzer.js

@@ -17,2 +17,321 @@ (function() {

{
module: 'window',
instance_methods: [
{
name: '$A',
message: '$A casts objects into arrays. It does not exist in Sugar, but Object#merge can achieve something similar:',
recommend_before: "$A({ 0: 'a', 1: 'b', 2: 'c' })",
recommend_after: "Object.merge([], { 0: 'a', 1: 'b', 2: 'c' })",
},
{
name: '$H',
message: '$H exists in Sugar as Object.extended.\nThis will return an extended object with instance methods on it similar to hashes in Prototype.\nKeep in mind, however, that the instance methods available to extended objects in Sugar do not 100% match those of Prototype.',
recommend_before: "$H({ 0: 'a', 1: 'b', 2: 'c' })",
recommend_after: "Object.extended({ 0: 'a', 1: 'b', 2: 'c' })",
},
{
name: '$R',
message: '$R (ObjectRanges) do not exist in Sugar.\nHowever, Number#upto or Number#downto achieve a very similar\neffect by creating arrays that contain that range:',
recommend_before: "$R(0, 10)",
recommend_after: "(0).upto(10)",
},
{
name: '$w',
message: '$w does not exist in Sugar.\nUse String#split instead:',
recommend_before: "$w('apples and oranges')",
recommend_after: "'apples and oranges'.split(' ')",
}
]
},
{
module: 'Number',
instance_methods: [
{
name: 'succ',
message: 'Number#succ does not exist in Sugar.\nThis is often done with the + operator :)',
recommend_before: "num.succ()",
recommend_after: "num + 1"
},
{
name: 'times',
condition: function() {
var type = typeof arguments[1];
return [arguments.length > 1 && type != 'number', arguments[1]];
},
message: 'Number#times does not accept a second parameter, but found {1}.\nIf you need to bind context, use Function#bind instead:',
recommend_before: "(8).times(function(){}, 'barf')",
recommend_after: "(8).times(function(){}.bind('barf'))",
},
{
name: 'toColorPart',
message: 'Number#toColorPart does not exist in Sugar.\nUse Number#hex and pad to 2 places instead:',
recommend_before: "(255).toColorPart()",
recommend_after: "(255).hex(2)",
},
{
name: 'toPaddedString',
message: 'Number#toPaddedString exists in Sugar as Number#pad.\nNote that in Sugar, the radix is the third parameter, as the second is to force the sign.',
recommend_before: "(20).toPaddedString(4, 2)",
recommend_after: "(20).pad(4, false, 2)",
}
]
/***
*
* Number#abs exists and is identical
* Number#ceil exists and will not break
* Number#floor exists and will not break
* Number#round exists and will not break
*
**/
},
{
module: 'String',
class_methods: [
{
name: 'interpret',
message: 'String.interpret does not exist in Sugar.\nUse String() instead. Note, however that this will not convert\nnull/undefined into a blank string, as is the case with String.interpret.',
recommend_before: "String.interpret(156)",
recommend_after: "String(156)"
}
],
instance_methods: [
{
name: 'blank',
message: 'String#blank exists in Sugar as String#isBlank:',
recommend_before: "'hello'.blank()",
recommend_after: "'hello'.isBlank()"
},
{
name: 'camelize',
condition: function() {
return /[ _]/.test(this);
},
message: 'String#camelize found white space or underscores!\nNote that #camelize in Sugar will remove whitespace and operate on underscores the same as dashes.'
},
{
name: 'dasherize',
condition: function() {
return /[A-Z\s]/.test(this);
},
message: 'String#dasherize found white space or capital letters!\nNote that #dasherize in Sugar will remove whitespace and operate on capital letters the same as underscores.'
},
{
name: 'empty',
message: 'String#empty does not exist in Sugar. Use a straight comparison instead.',
recommend_before: "str.empty()",
recommend_after: "str === ''",
},
{
name: 'evalJSON',
message: 'String#evalJSON does not exist in Sugar.\nJSON.parse may work as an alternative, but it is not available in all browsers.\nIf you absolutely require JSON support, consider adding in a separate library such as:\nhttps://github.com/douglascrockford/JSON-js',
recommend_before: "str.evalJSON()",
recommend_after: "JSON.parse(str)"
},
{
name: 'evalScripts',
message: "String#evalScripts does not exist in Sugar.\nIt's highly unlikely that you should be doing something like this anyway,\nbut if you really need to in a pinch, something like this may work:",
recommend_before: "str.evalScripts()",
recommend_after: "str.match(/<script.*?>.+?<\/script>/g).map(function(m){ return eval(m.replace(/<\/?script.*?>/g, '')); })",
},
{
name: 'extractScripts',
message: 'String#extractScripts does not exist in Sugar.\nIf you really need to do this, then in a pinch something like this may work:',
recommend_before: "str.extractScripts()",
recommend_after: "str.match(/<script.*?>.+?<\/script>/g).map(function(m){ return m.replace(/<\/?script.*?>/g, ''); })",
},
{
name: 'gsub',
message: 'String#gsub does not exist in Sugar.\nJust use the native .replace function instead.\nNote that Javascript allows functions to be passed to .replace just like gsub:',
recommend_before: "'Image: (img.png)'.gsub(/Image: \(.+\)/, function(match, src){ return '<img src=\"' + src + '\" />'; })",
recommend_after: "'Image: (img.png)'.replace(/Image: \(.+\)/, function(match, src){ return '<img src=\"' + src + '\" />'; })"
},
{
name: 'include',
message: 'String#include exists in Sugar as String#has:',
recommend_before: "'foobar'.include('bar')",
recommend_after: "'foobar'.has('bar')"
},
{
name: 'inspect',
message: 'String#inspect does not exist in Sugar. Consider using JSON.stringify(str) instead.\nThe JSON global does not exist in all implementations but should be enough to get you through a debug session:',
recommend_before: "'foofa'.inspect()",
recommend_after: "JSON.stringify('foofa')"
},
// TODO: ADD THIS??
{
name: 'interpolate',
message: 'String#interpolate does not exist in Sugar.\nThis may be added in at a later date.\nFor now, String#replace can serve as a (primitive) alternative:',
recommend_before: "'i like #{fruit}'.interpolate({ fruit: 'peaches' })",
recommend_after: "'i like #{fruit}'.replace(/#{fruit}/, 'peaches')"
},
{
name: 'isJSON',
message: 'String#isJSON does not exist in Sugar.\nThe simplest way of determining if a value is JSON or not is to attempt parsing and catch errors.\nIf you absolutely require full JSON support, consider adding in a separate library such as:\nhttps://github.com/douglascrockford/JSON-js',
recommend_before: "valid = str.isJSON()",
recommend_after: "try { JSON.parse(str); valid = true; } catch(e){ valid = false; }"
},
{
name: 'scan',
message: 'String#scan exists in Sugar as String#each:',
recommend_before: "'apple, pear & orange'.scan(/\w+/, console.log)",
recommend_after: "'apple, pear & orange'.each(/\w+/, console.log)"
},
{
name: 'strip',
message: 'String#strip exists in Sugar as String#trim:',
recommend_before: "' howdy '.strip()",
recommend_after: "' howdy '.trim()"
},
{
name: 'stripScripts',
message: 'String#stripScripts can be achieved in Sugar with String#removeTags:',
recommend_before: "'<script>doEvilStuff();</script>'.stripScripts()",
recommend_after: "'<script>doEvilStuff();</script>'.removeTags('script')"
},
{
name: 'stripTags',
condition: function() {
return arguments.length == 0 && /<.*?:.*?>/.test(this);
},
message: 'String#stripTags found namespaced tags such as <xsl:template>. Be aware that Sugar will strip these tags too!'
},
{
name: 'sub',
message: 'String#sub does not exist in Sugar.\nStandard Javascript .replace is the closest approximation.\nIf you need to replace more than one occurrence of a pattern (but not all), your best bet is to set a counter and test against it.',
recommend_before: "'one two three four'.sub(' ', ', ', 2)",
recommend_after: "var c = 0; 'one two three four'.replace(/\s/g, function(m, i){ c++; return c < 3 ? ', ' : ' '; })"
},
{
name: 'succ',
message: 'String#succ exists in Sugar as String#shift, which can move up or down the Unicode range by a number which is the first argument passed.',
recommend_before: "'a'.succ()",
recommend_after: "'a'.shift(1);"
},
{
name: 'times',
message: 'String#times exists in Sugar as String#repeat:',
recommend_before: "'echo '.times(3)",
recommend_after: "'echo '.repeat(3);"
},
{
name: 'toArray',
message: 'String#toArray splits a string into an array of characters.\nThe same effect can be achieved in Sugar by calling String#chars:',
recommend_before: "'howdy'.toArray()",
recommend_after: "'howdy'.chars();"
},
{
name: 'toQueryParams',
message: 'String#toQueryParams exists in Sugar as String#toObject.\nThis method allows two tokens to turn a string into a key/value hash.\nThe default tokens are exactly those for parsing out a URL string, so no parameters are required.',
recommend_before: "'section=blog&id=45'.toQueryParams()",
recommend_after: "'section=blog&id=45'.toObject()"
},
{
name: 'truncate',
message: 'String#truncate does not exist in Sugar.\nUse String#slice instead:',
recommend_before: "longString.truncate(10, '...')",
recommend_after: "longString.slice(0, 10) + '...'"
},
{
name: 'underscore',
condition: function() {
return /\s/.test(this);
},
message: 'String#underscore found white space!\nNote that underscore in Sugar will remove all whitespace.'
},
{
name: 'unfilterJSON',
message: 'String#unfilterJSON does not exist in Sugar.'
}
]
/***
*
* String#capitalize exists and will not break
* String#endsWith exists and will not break
* String#escapeHTML exists and is identical
* String#startsWith exists and will not break
* String#unescapeHTML exists and is identical
*
**/
},
{
module: 'Hash',
instance_methods: [
{
name: 'each',
condition: function() {
var type = typeof arguments[1];
return [arguments.length > 1 && type != 'number', arguments[1]];
},
message: 'Second argument to .each should be a number but instead was {1}. If you need to bind context, use Function#bind instead:',
recommend_before: "new Hash().each(function(){}, 'context')",
recommend_after: "Object.extended().each(function(){}.bind('context'))",
},
{
name: 'get',
message: 'Sugar extended objects do not have a "get" method.\nSimply access the property as you would a normal object literal.',
recommend_before: "var h = new Hash({ foo: 'bar' }); h.get('foo')",
recommend_after: "var h = Object.extended({ foo: 'bar' }); h['foo']",
},
{
name: 'index',
message: 'Sugar extended objects do not have an "index" method.\nTo find a key in an extended object, you will have to iterate over it manually.',
recommend_before: "var key = new Hash({ foo: 'bar' }).index('bar')",
recommend_after: "Object.extended({ foo: 'bar' }).each(function(k, v){ if(v == 'bar') var key = k; })",
},
{
name: 'inspect',
message: 'Sugar extended objects do not have an "inspect" method.\nConsider using JSON.stringify() instead.\nThe JSON global does not exist in all implementations but should be enough to get you through a debug session:',
recommend_before: "new Hash({ foo: 'bar' }).inspect()",
recommend_after: "JSON.stringify(Object.extended({ foo: 'bar' }))"
},
{
name: 'merge',
message: 'Sugar extended have a "merge" method, however they merge the incoming object onto the original and modify it.\nIf you need to create a clone, use the "clone" method first.',
recommend_before: "new Hash({ foo: 'bar' }).merge({ moo: 'car' })",
recommend_after: "Object.extended({ foo: 'bar' }).clone().merge({ moo: 'car' })"
},
{
name: 'set',
message: 'Sugar extended objects do not have a "set" method.\nSimply set the property as you would a normal object literal.',
recommend_before: "var h = new Hash({ foo: 'bar' }); h.set('moo', 'car')",
recommend_after: "var h = Object.extended({ foo: 'bar' }); h['moo'] = 'car'",
},
{
name: 'toJSON',
message: 'Sugar extended objects do not have a "toJSON" method. \nJSON.stringify may work as an alternative, but it is not available in all browsers.\nIf you absolutely require JSON support, consider adding in a separate library such as:\nhttps://github.com/douglascrockford/JSON-js',
recommend_before: "Object.toJSON(obj)",
recommend_after: "JSON.stringify(obj)"
},
{
name: 'toObject',
message: 'Sugar extended objects do not have a "toObject" method, as they already behave like vanilla objects.'
},
{
name: 'toTemplateReplacements',
message: 'Sugar extended objects do not have a "toTemplateReplacements" method.\nThis method is not necessary as extended objects already behave like vanilla objects.'
},
{
name: 'unset',
message: 'Sugar extended objects do not have an "unset" method.\nSimply delete the property as you would a normal object literal.',
recommend_before: "var h = new Hash({ foo: 'bar' }); h.unset('foo')",
recommend_after: "var h = Object.extended({ foo: 'bar' }); delete h.foo",
},
{
name: 'update',
message: 'Sugar extended objects do not have an "update" method.\nAs this merges objects into the hash in place, simply use the "merge" method instead.',
recommend_before: "new Hash({ foo: 'bar' }).merge({ moo: 'car' })",
recommend_after: "Object.extended({ foo: 'bar' }).merge({ moo: 'car' })"
},
/***
*
* Hash#clone exists and is identical
* Hash#keys exists and is identical
* Hash#values exists and is identical
*
* Hash#toQueryString is for DOM
*
**/
]
},
{
module: 'Object',

@@ -32,4 +351,49 @@ class_methods: [

},
message: 'Object.clone was called on a deep (nested) object. Be aware that Sugar will make DEEP copies of such objects. This is different from Prototype, which makes shallow copies. Additionally, Prototype will clone properties in the prototype chain which Sugar does not do.'
message: 'Object.clone was called on a deep (nested) object. Be aware that Sugar will make DEEP copies of such objects. This is different from Prototype, which makes shallow copies. Additionally, Prototype will clone properties in the prototype chain which Sugar will not do.'
},
{
name: 'extend',
message: 'Object.extend does not exist in Sugar. Use Object.merge instead:',
recommend_before: "Object.extend( { a: 1 }, { b: 2 })",
recommend_after: "Object.merge( { a: 1 }, { b: 2 })",
},
{
name: 'inspect',
message: 'Object.inspect does not exist in Sugar. Consider using JSON.stringify(object) instead.\nThe JSON global does not exist in all implementations but should be enough to get you through a debug session:',
recommend_before: "Object.inspect([1,2,3])",
recommend_after: "JSON.stringify([1,2,3])"
},
{
name: 'isHash',
message: 'Object.isHash does not exist in Sugar. Use Object.isObject instead:',
recommend_before: "Object.isHash({ a: 1 })",
recommend_after: "Object.isObject({ a: 1 })"
},
{
name: 'isUndefined',
message: 'Object.isUndefined does not exist in Sugar. Use straight Javascript instead:',
recommend_before: "Object.isUndefined(obj)",
recommend_after: "obj === undefined"
},
{
name: 'toJSON',
message: 'Object.toJSON does not exist in Sugar.\nJSON.stringify may work as an alternative, but it is not available in all browsers.\nIf you absolutely require JSON support, consider adding in a separate library such as:\nhttps://github.com/douglascrockford/JSON-js',
recommend_before: "Object.toJSON(obj)",
recommend_after: "JSON.stringify(obj)"
}
/***
*
* Object.isArray exists and is identical
* Object.isDate exists and is identical
* Object.isFunction exists and is identical
* Object.isNumber exists and is identical
* Object.isString exists and is identical
* Object.keys exists and is identical
* Object.values exists and is identical
*
* Object.isElement is for DOM
* Object.toHTML is for DOM
* Object.toQueryString is for DOM
*
**/
]

@@ -39,5 +403,25 @@ },

module: 'Array',
class_methods: [
{
name: 'from',
message: 'Array.from does not exist in Sugar. Object.merge can accomplish similar functionality:',
recommend_before: "Array.from({ 0: 'a', 1: 'b', 2: 'c' })",
recommend_after: "Object.merge([], { 0: 'a', 1: 'b', 2: 'c' })",
}
],
instance_methods: [
{
name: 'find',
name: 'collect',
message: 'Enumerable#collect does not exist in Sugar. Use Array#map instead:',
recommend_before: "[1,2,3].collect(function(n){ return n * 2; })",
recommend_after: "[1,2,3].map(function(n){ return n * 2; })",
},
{
name: 'detect',
message: 'Enumerable#detect does not exist in Sugar. Use Array#find instead:',
recommend_before: "[1,2,3].detect(function(n){ return n > 1; })",
recommend_after: "[1,2,3].find(function(n){ return n > 1; })",
},
{
name: 'each',
condition: function() {

@@ -47,8 +431,21 @@ var type = typeof arguments[1];

},
message: 'Second argument to .find should be a number but instead was {1}',
recommend_before: "['a','b','c'].find(function(){}, 'context');",
recommend_after: "['a','b','c'].find(function(){}.bind('context'));"
message: 'Second argument to .each should be a number but instead was {1}. If you need to bind context, use Function#bind instead:',
recommend_before: "['a','b','c'].each(function(){}, 'context')",
recommend_after: "['a','b','c'].each(function(){}.bind('context'))",
one_time_warning: 'Caution: If a callback passed to Array#each returns false, it will break out of the loop.'
},
{
name: 'findAll',
name: 'eachSlice',
message: 'Enumerable#eachSlice does not exist in Sugar. Use Array#inGroupsOf instead:',
recommend_before: "[1,2,3,4].eachSlice(2, function(){})",
recommend_after: "[1,2,3,4].inGroupsOf(2).each(function(){})"
},
{
name: 'entries',
message: 'Enumerable#entries does not exist in Sugar. Use Array#clone instead:',
recommend_before: "[1,2,3].entries()",
recommend_after: "[1,2,3].clone()"
},
{
name: 'find',
condition: function() {

@@ -58,8 +455,8 @@ var type = typeof arguments[1];

},
message: 'Second argument to .findAll should be a number but instead was {1}',
recommend_before: "['a','b','c'].findAll(function(){}, 'context');",
recommend_after: "['a','b','c'].findAll(function(){}.bind('context'));"
message: 'Second argument to Array#find should be a number but instead was {1}. If you need to bind context, use Function#bind instead:',
recommend_before: "['a','b','c'].find(function(){}, 'context')",
recommend_after: "['a','b','c'].find(function(){}.bind('context'))"
},
{
name: 'each',
name: 'findAll',
condition: function() {

@@ -69,16 +466,11 @@ var type = typeof arguments[1];

},
message: 'Second argument to .each should be a number but instead was {1}',
recommend_before: "['a','b','c'].each(function(){}, 'context');",
recommend_after: "['a','b','c'].each(function(){}.bind('context'));",
one_time_warning: 'Caution: If a callback passed to Array#each returns false, it will break out of the loop.'
message: 'Second argument to Array#findAll should be a number but instead was {1}. If you need to bind context, use Function#bind instead:',
recommend_before: "['a','b','c'].findAll(function(){}, 'context')",
recommend_after: "['a','b','c'].findAll(function(){}.bind('context'))"
},
{
name: 'compact',
condition: function() {
for(var i = 0; i < this.length; i++){
if(isNaN(this[i])) return true;
}
return false;
},
message: '.compact was called on an array that contains NaN values. Sugar will remove these from the array while Prototype leaves them alone.'
name: 'grep',
message: 'Enumerable#grep does not exist in Sugar. Use Array#findAll instead:',
recommend_before: "['a','b','c'].grep(/[ab]/)",
recommend_after: "['a','b','c'].findAll(/[ab]/)"
},

@@ -90,23 +482,67 @@ {

},
message: 'Array#include in Protype detects if an element is in the array and returns true/false.\nIn Sugar this method includes the argument passed into the array.\nIt is a reciprocal of Array#remove, and a non-destructive mirror method of Array#add.'
message: 'Enumerable#include in Protype detects if an element is in the array and returns true/false.\nIn Sugar this method instead adds the argument passed to the array without modifying it.\nArray#include is a reciprocal of Array#exclude, and a non-destructive version of Array#add.\nUse Array#has instead for equivalent functionality.',
recommend_before: "[1,2,3].include(1)",
recommend_after: "[1,2,3].has(1)"
},
{
name: 'min',
name: 'inject',
message: 'Enumerable#inject does not exist in Sugar. Use Javascript native Array#reduce to achieve the same effect:',
recommend_before: '[1,2,3,4].inject(100, function(a, b){ return a + b; });',
recommend_after: '[1,2,3,4].reduce(function(a, b){ return a + b; }, 100);'
},
{
name: 'invoke',
message: 'Enumerable#invoke does not exist in Sugar. Use Array#map to achieve the same effect:',
recommend_before: "['hello','world'].invoke('toUpperCase')",
recommend_after: "['hello','world'].map('toUpperCase')"
},
{
name: 'max',
condition: function(f) {
return arguments.length > 0;
},
message: 'Use caution when using Array#min:\n\n(1) Sugar will return an array of minimum values (as there can be more than one), where Prototype only returns the first value.\n(2) When using iterators, Prototype will return the value compared, where Sugar will return the actual array element itself.\n(3) Finally, Sugar does not allow a context to be passed.',
recommend_before: "[{ a: 5 },{ a: 10 }].min(function(el){ return el['a']; }, 'context');",
recommend_after: "[{ a: 5 },{ a: 10 }].min(function(el){ return el['a']; }.bind('context')).first().a;"
message: 'Use caution when using Enumerable#max:\n\n(1) Sugar will return an array of maximum values (as there can be more than one), where Prototype only returns the first value.\n(2) When using iterators, Prototype will return the value compared, where Sugar will return the actual array element itself.\n(3) Finally, Sugar does not allow a context to be passed.',
recommend_before: "[{ a: 5 },{ a: 10 }].max(function(el){ return el['a']; }, 'context')",
recommend_after: "[{ a: 5 },{ a: 10 }].max(function(el){ return el['a']; }.bind('context')).first().a"
},
{
name: 'max',
name: 'member',
message: 'Enumerable#member does not exist in Sugar. Use Array#has instead:',
recommend_before: "[1,2,3].member(1)",
recommend_after: "[1,2,3].has(1)"
},
{
name: 'min',
condition: function(f) {
return arguments.length > 0;
},
message: 'Use caution when using Array#max:\n\n(1) Sugar will return an array of maximum values (as there can be more than one), where Prototype only returns the first value.\n(2) When using iterators, Prototype will return the value compared, where Sugar will return the actual array element itself.\n(3) Finally, Sugar does not allow a context to be passed.',
recommend_before: "[{ a: 5 },{ a: 10 }].max(function(el){ return el['a']; }, 'context');",
recommend_after: "[{ a: 5 },{ a: 10 }].max(function(el){ return el['a']; }.bind('context')).first().a;"
message: 'Use caution when using Enumerable#min:\n\n(1) Sugar will return an array of minimum values (as there can be more than one), where Prototype only returns the first value.\n(2) When using iterators, Prototype will return the value compared, where Sugar will return the actual array element itself.\n(3) Finally, Sugar does not allow a context to be passed.',
recommend_before: "[{ a: 5 },{ a: 10 }].min(function(el){ return el['a']; }, 'context')",
recommend_after: "[{ a: 5 },{ a: 10 }].min(function(el){ return el['a']; }.bind('context')).first().a"
},
{
name: 'partition',
message: "Enumerable#partition does not exist in Sugar.\nArray#group however has similar functionality, and may be a suitable alternative.\nIt will create a hash with keys based on the return values of the iterator, with each grouping as the value.\nInstead of accessing the split array, you can access the hash by these keys.\nThis method has the added advantage that it can also split into more than two groups.",
recommend_before: "[1,2,3,4,5,6].partition(function(n){ return n % 2 === 0; })",
recommend_after: "[1,2,3,4,5,6].group(function(n){ return n % 2 === 0 ? 'even' : 'odd'; })"
},
{
name: 'pluck',
message: 'Enumerable#pluck does not exist in Sugar. Use Array#map instead:',
recommend_before: "['hello','world'].pluck('length')",
recommend_after: "['hello','world'].map('length')"
},
{
name: 'reject',
message: "Enumerable#reject does not exist in Sugar. Its equivalent is Array#exclude.\nThis is a non-destructive way to remove elements from an array.\nIf you want a destructive version, use Array#remove instead.\nAlso note these methods' reciprocals: Array#include and Array#add.",
recommend_before: "[1,2,3].reject(function(n){ n < 3; })",
recommend_after: "[1,2,3].exclude(function(n){ n < 3; })"
},
{
name: 'select',
message: 'Enumerable#select does not exist in Sugar. Use Array#findAll instead:',
recommend_before: "[1,2,3].select(function(n){ n < 3; })",
recommend_after: "[1,2,3].findAll(function(n){ n < 3; })"
},
{
name: 'sortBy',

@@ -117,18 +553,154 @@ condition: function(f, scope) {

},
message: 'Second argument to .sortBy should be a boolean but instead was {1}',
recommend_before: "[{ a: 5 },{ a: 10 }].sortBy(function(el){ return el['a']; });",
recommend_after: "[{ a: 5 },{ a: 10 }].sortBy(function(el){ return el['a']; }.bind('context'));"
message: 'Second argument to .sortBy should be a boolean but instead was {1}. If you need to bind context, use Function#bind instead:',
recommend_before: "[{ a: 5 },{ a: 10 }].sortBy(function(el){ return el['a']; })",
recommend_after: "[{ a: 5 },{ a: 10 }].sortBy(function(el){ return el['a']; }.bind('context'))"
},
{
name: 'collect',
alternate: 'map'
name: 'size',
message: 'Enumerable#size does not exist in Sugar. Just use array.length!',
recommend_before: "[1,2,3].size()",
recommend_after: "[1,2,3].length"
},
{
name: 'detect',
alternate: 'find'
name: 'toArray',
message: 'Enumerable#toArray does not exist in Sugar. Use Array#clone instead:',
recommend_before: "[1,2,3].toArray()",
recommend_after: "[1,2,3].clone()"
},
{
name: 'zip',
message: 'Enumerable#zip does not exist in Sugar. Array#map can easily achieve the same functionality, however:',
recommend_before: "firstNames.zip(lastNames)",
recommend_after: "firstNames.map(function(name, index){ return [name, lastNames[index]]; })"
},
{
name: 'compact',
condition: function() {
for(var i = 0; i < this.length; i++){
if(isNaN(this[i])) return true;
}
return false;
},
message: 'Caution: Array#compact was called on an array that contains NaN values. Sugar will remove these from the array while Prototype leaves them alone:'
},
{
name: 'clear',
message: 'Array#clear does not exist in Sugar. Use array = [] instead:',
recommend_before: "f.clear()",
recommend_after: "f = []"
},
{
name: 'inspect',
message: 'Array#inspect does not exist in Sugar. Consider using JSON.stringify(array) instead.\nThe JSON global does not exist in all implementations but should be enough to get you through a debug session:',
recommend_before: "[1,2,3].inspect()",
recommend_after: "JSON.stringify([1,2,3])"
},
{
name: 'reverse',
condition: function(inline) {
return inline === false;
},
message: 'Array#reverse exists in native Javascript, but is destructive. Try cloning the array first:',
recommend_before: "array.reverse(false)",
recommend_after: "array.clone().reverse()"
},
{
name: 'uniq',
message: 'Array#uniq exists in Sugar as Array#unique:',
recommend_before: "[1,1,1].uniq()",
recommend_after: "[1,1,1].unique()"
},
{
name: 'without',
message: 'Array#without exists in Sugar as Array#exclude:',
recommend_before: "[1,2,3].without(3)",
recommend_after: "[1,2,3].exclude(3)"
}
/***
*
* Enumerable#all exists and is identical
* Enumerable#any exists and is identical
* Enumerable#every exists and is identical
* Enumerable#filter exists and is identical
* Enumerable#inGroupsOf exists and is identical
* Enumerable#inspect is the same as Array#inspect here
* Enumerable#map exists and is identical
* Enumerable#size is the same as Array#size here
* Enumerable#some exists and is identical
* Enumerable#toArray is the same as Array#toArray here
*
* Array#clone exists and is identical
* Array#compact also removes NaN
* Array#first exists and will work with no arguments
* Array#flatten exists and is identical
* Array#indexOf polyfill exists and is identical
* Array#intersect exists and will work with 1 argument
* Array#last exists and will work with no arguments
* Array#lastIndexOf polyfill exists and is identical
*
**/
]
},
{
module: 'Function',
instance_methods: [
{
name: 'argumentNames',
message: 'Function#argumentNames does not exist in Sugar.',
},
{
name: 'bindAsEventListener',
message: 'Function#bindAsEventListener does not exist in Sugar.\nIf this is a problem, it can be easily sidestepped.\nFirst, Function#bind will function identically as long as you are not currying arguments.\nIf you are currying arguments you will still receive the event, it will just be at the end of the argument array instead of the beginning.\nAs long as you keep this in mind you should be able to refactor to use .bind instead:',
recommend_before: "var fn = function(event, one) {\n\t // this will be \"bound\"\n\t // event will be the event\n\t // one will be 1\n\t}\n\tfn.bindAsEventListener('bound', 1)\n",
recommend_after: "var fn = function(one, event) {\n\t // this will be \"bound\"\n\t // one will be 1\n\t // event will be the event (comes last)\n\t}\n\tfn.bind('bound', 1)",
},
{
name: 'curry',
message: 'Function#curry does not exist in Sugar.\nSimply use Function#bind with null as the first argument:',
recommend_before: "fn.curry('one','two')",
recommend_after: "fn.bind(null, 'one', 'two')",
},
{
name: 'defer',
message: 'Function#defer exists in Sugar as Function#lazy.\nWhen no params are passed it will behave precisely the same as calling the function with a timeout of 1 ms.',
recommend_before: "fn.defer()",
recommend_after: "fn.lazy()"
},
{
name: 'delay',
message: 'Function#delay exists in Sugar, but is slightly different.\nFirst, the delay is passed in milliseconds, not seconds.\nSecond, delay will return a reference to the function instead of an integer to clear the timeout.\nIf you need to cancel the timeout, instead use Function#cancel.\nArguments passed after the timeout are still curried like Prototype.',
recommend_before: "var t = fn.delay(2)\n\tclearTimeout(t)\n",
recommend_after: "fn.delay(2000)\n\tfn.cancel()",
},
{
// MIGHT want this... not sure...
name: 'methodize',
message: 'Function#methodize does not exist in Sugar.\nNo direct equivalent exists, but in a pinch the following code will achieve the same effect.\nIf this is an important method you use all the time,\nplease let me know via GitHub (https://github.com/andrewplummer/Sugar) and I will consider adding it.',
recommend_before: "obj.method = fn.methodize()",
recommend_after: "obj.method = function(){ fn.apply(null, [this].concat(Array.prototype.slice.call(arguments))); }"
},
{
// MIGHT want this... not sure...
name: 'wrap',
message: 'Function#wrap does not exist in Sugar.\nNo direct equivalent exists, but in a pinch the following code will achieve the same effect.\nIf this is an important method you use all the time,\nplease let me know via GitHub (https://github.com/andrewplummer/Sugar) and I will consider adding it.',
recommend_before: "fn = fn.wrap(function(original){ // do some stuff })",
recommend_after: "fn = (function(o){ return function(original){ // do some stuff }.bind(null, o); })(fn)"
}
]
/***
*
* Function#bind exists and is identical
*
* */
}
];
/***
*
* Date#toISOString exists and is identical
* Date#toJSON exists and is identical
*
* */
var TS = ' ';

@@ -138,5 +710,10 @@ var warn = function(message, level, skipMeta, docs) {

stack = new Error().stack;
message = message.replace(/\t/g, TS);
if(stack) {
match = stack.split('\n')[level].match(/@(.+):(\d+)$/);
file = match[1];
if(file.match(/prototype[^/]+$/)) {
// Assumed to be internally called method, so don't display warnings in this case.
return;
}
line = match[2];

@@ -170,16 +747,19 @@ if(!skipMeta) {

var wrapMethod = function(module, collision, instance) {
var m = instance ? window[module].prototype : window[module];
var m;
if(module == 'window') {
m = window;
} else {
m = instance ? window[module].prototype : window[module];
}
var fn = m[collision.name], message = collision.message;
m[collision.name] = function() {
var r = collision.condition ? collision.condition.apply(this, arguments) : true;
if(collision.alternate){
message = module + '#' + collision.name + ' does not exist in Sugar. Use ' + module + '#' + collision.alternate + ' instead.';
}
if(r === true || (r.length && r[0] === true)) {
if(message && (r === true || (r.length && r[0] === true))) {
message = supplant(message, r);
if(collision.recommend_before){
message += '\n\nSugar equivalent:\n';
message += '\nthis: ' + collision.recommend_before;
message += '\nbecomes: ' + collision.recommend_after;
//message += '\n\nSugar equivalent...\n';
message += '\n\n';
message += '\nPrototype: ' + collision.recommend_before;
message += '\nSugar: ' + collision.recommend_after;
message += '\n';
}

@@ -190,3 +770,3 @@ var ref = 'http://sugarjs.com/api/#!/' + module + '#' + (collision.alternate || collision.name);

if(collision.one_time_warning && !collision.warned) {
warn(collision.one_time_warning, 3, true);
warn(collision.one_time_warning, 3, false);
collision.warned = true;

@@ -202,9 +782,23 @@ }

val = obj[d];
return val !== undefined ? val : m;
return val !== undefined ? jsonify(val) : m;
});
}
function jsonify(o){
if(typeof JSON != 'undefined') {
return JSON.stringify(o);
} else {
return o.toString();
}
}
var initialize = function() {
var welcome =
'### Welcome to the Sugar analyzer script! ###\n\n' +
'As your program calls various methods, it will warn you about incompatibilities with Sugar, and give suggestions\n' +
'about how to refactor. You can run this before refactoring to get a general idea about what needs to change,\n' +
'or you can flip out Prototype for Sugar, let breakages happen, and fix as you go!\n\n';
console.info(welcome);
if(typeof Prototype != 'undefined') {

@@ -211,0 +805,0 @@ wrapMethods(PrototypeCollisions);

4

package.json
{
"name": "sugar",
"version": "0.9.4",
"version": "0.9.5",
"description": "A Javascript library for working with native objects.",
"keywords": ["functional", "utility"],
"homepage": "http://sugarjs.com",
"homepage": "http://sugarjs.com/",
"author": "Andrew Plummer",

@@ -8,0 +8,0 @@ "main": "./lib/sugar",

@@ -9,2 +9,4 @@ Sugar

Unit Tests Node

@@ -22,1 +24,51 @@ ===============

./unit_tests/node.sh
Note about using Dates as a standalone module
=============================================
The Date module depends on a number of Sugar methods. It can be used on its own, but you will
have to keep the following dependencies in addition to this module. The Array Module's polyfill methods can be
skipped if you don't care about < IE8 or if you are using another library that provides them. Finally, you
must keep "buildDate" in the initialization script at the very bottom of the file.
### Global private methods (at the top of the file)
- extend
- extendWithNativeCondition
- wrapNative
- defineProperty
- iterateOverObject
### Object private methods
- instance
- typeMethods
- buildTypeMethods
### Number instance methods
- ordinalize
- pad
### String private methods
- padstring
- NPCGMatch
### String instance methods
- capitalize
- first
- from
- repeat
- to
### Array instance methods (polyfill)
- indexOf
- filter
- forEach
- map

@@ -614,5 +614,38 @@

// Polyfills can be run on objects that inherit from Arrays
var Soup = function(){};
Soup.prototype = [1,2,3];
var x = new Soup();
count = 0;
x.every(function() {
count++;
return true;
});
x.some(function() {
count++;
});
x.map(function() {
count++;
});
x.filter(function() {
count++;
});
x.forEach(function() {
count++;
});
x.reduce(function() {
count++;
});
x.reduceRight(function() {
count++;
});
equal(count, 19, 'Array | array elements in the prototype chain are also properly iterated');
equal(x.indexOf(2), 1, 'Array | indexOf | array elements in the prototype chain are also properly iterated');
equal(x.lastIndexOf(2), 1, 'Array | lastIndexOf | array elements in the prototype chain are also properly iterated');
// String#trim

@@ -619,0 +652,0 @@

@@ -28,4 +28,2 @@ test('Function', function () {

var delayedFunction, delayReturn, shouldBeFalse;
// Prototype's delay function takes the value in seconds, so 20 makes the tests

@@ -35,52 +33,154 @@ // take at least 20 seconds to finish!

delayedFunction = function(one, two) {
equal(one, 'one', 'Function#delay | first parameter', { mootools: 'two' });
equal(two, 'two', 'Function#delay | second parameter', { mootools: undefined });
equal(shouldBeFalse, false, 'Function#delay | cancel is working', { prototype: true, mootools: true });
//start();
};
(function(){
var fn, ref;
fn = function(one, two) {
equal(one, 'one', 'Function#delay | first parameter', { mootools: 'two' });
equal(two, 'two', 'Function#delay | second parameter', { mootools: undefined });
};
ref = fn.delay(delayTime, 'one', 'two');
equal(typeof ref, 'function', 'Function#delay | returns the function', { prototype: 'number', mootools: 'number' });
})();
delayReturn = delayedFunction.delay(delayTime, 'one', 'two');
equal(typeof delayReturn, nativeTimeoutReturnType, 'Function#delay | returns the timeout ID');
(function(){
var fn, ref, shouldBeFalse = false;
fn = function() {
shouldBeFalse = true;
};
fn.delay(delayTime / 4);
fn.delay(delayTime / 5);
fn.delay(delayTime / 6);
ref = fn.cancel();
equal(ref, fn, 'Function#cancel | returns a reference to the function');
setTimeout(function() {
equal(shouldBeFalse, false, 'Function#delay | cancel is working', { prototype: true, mootools: true });
}, 60);
})();
shouldBeFalse = false;
delayedFunction = function() {
shouldBeFalse = true;
};
// Properly unit testing Function#lazy will probably be a bitch...
// Will have to rethink strategy here.
(function() {
var counter = 0;
var expected = [['maybe','a',1],['baby','b',2],['you lazy','c',3]];
var fn = (function(one, two) {
equal([this.toString(), one, two], expected[counter], 'Function#lazy | scope and arguments are correct');
counter++;
}).lazy();
fn.call('maybe', 'a', 1);
fn.call('baby', 'b', 2);
fn.call('you lazy', 'c', 3);
equal(counter, 0, "Function#lazy | hasn't executed yet");
setTimeout(function() {
equal(counter, 3, 'Function#lazy | was executed by 10ms');
}, 10);
})();
delayReturn = delayedFunction.delay(delayTime / 4);
delayedFunction.cancel();
(function() {
var counter = 0;
var fn = (function() { counter++; }).lazy();
fn();
fn();
fn.cancel();
setTimeout(function() {
equal(counter, 0, 'Function#lazy | lazy functions can also be canceled');
}, 10);
})();
bound = (function(num, bool, str) {}).delay(1, 'wasabi');
bound = (function(one, two) {
equal(this, 'poo', 'Function#defer | bound object');
equal(one, 'one', 'Function#defer | first parameter', { mootools: 'two' });
equal(two, 'two', 'Function#defer | second parameter', { mootools: undefined });
}).bind('poo').defer('one', 'two');
bound = (function(num, bool, str) {}).defer('three');
// Properly unit testing the exact throttle of Function.lazy will probably be a bitch...
// Will have to rethink strategy here.
var lazyCounter = 0;
var lazy = Function.lazy(function() {
lazyCounter++;
});
lazy();
lazy();
lazy();
equal(lazyCounter, 0, "Function.lazy | hasn't executed yet");
setTimeout(function() {
equal(lazyCounter, 3, 'Function.lazy | was executed by 10ms');
}, 10);
// Function#debounce
// Giving this it's own scope + a timeout here as it seems to make this
// temperamental test happier to run after other execution (GC?) has finished.
//stop();
setTimeout(function(){
var fn, ret, counter = 0; expected = [['leia', 5],['han solo', 7]];
var fn = (function(one){
equal([this.toString(), one], expected[counter], 'Function#debounce | scope and arguments are correct');
counter++;
}).debounce(50);
fn.call('3p0', 1);
fn.call('r2d2', 2);
fn.call('chewie', 3);
setTimeout(function() {
fn.call('leia', 5);
}, 10);
setTimeout(function() {
fn.call('luke', 6);
fn.call('han solo', 7);
}, 100);
ret = fn.call('vader', 4);
equal(ret, undefined, 'Function#debounce | calls to a debounced function return undefined');
setTimeout(function() {
equal(counter, 2, 'Function#debounce | counter is correct');
}, 200);
}, 1);
// Function#after
(function() {
var fn, ret, counter = 0, i = 1;
fn = (function() {
counter++;
return 'hooha';
}).after(5);
while(i <= 10) {
ret = fn();
equal(ret, (i % 5 == 0 ? 'hooha' : undefined), 'Function#after | collects return value as well');
i++;
}
equal(counter, 2, 'Function#after | calls a function only after a certain number of calls');
})();
// Function#once
(function() {
var fn, obj = { foo:'bar' }, counter = 0;
fn = (function(one, two) {
counter++;
equal(this, obj, 'Function#once | scope is properly set');
equal(one, 'one', 'Function#once | first argument is passed');
equal(two, 'two', 'Function#once | second argument is passed');
return counter * 30;
}).once();
equal(fn.call(obj, 'one', 'two'), 30, 'Function#once | first call calculates the result');
equal(fn.call(obj, 'one', 'two'), 30, 'Function#once | second call memoizes the result');
equal(fn.call(obj, 'one', 'two'), 30, 'Function#once | third call memoizes the result');
equal(fn.call(obj, 'one', 'two'), 30, 'Function#once | fourth call memoizes the result');
equal(fn.call(obj, 'one', 'two'), 30, 'Function#once | fifth call memoizes the result');
equal(counter, 1, 'Function#once | counter is only incremented once');
})();
(function() {
var fn, counter = 0;
fn = (function(one, two) {
counter++;
}).once();
fn.call();
fn.call();
fn.call();
equal(counter, 1, 'Function#once | returning undefined will not affect the number of calls');
})();
});

@@ -123,6 +123,14 @@

ret = (5).upto(1, function() {});
equal(counter, 0, 'Number#downto | 5 up to 1 | iterates 0 times');
equal(ret, [], 'Number#downto | 5 up to 1 | returns empty array');
equal(counter, 0, 'Number#upto | 5 up to 1 | iterates 0 times');
equal(ret, [], 'Number#upto | 5 up to 1 | returns empty array');
equal((3).upto(9, null, 3), [3,6,9], 'Number#upto | can handle multiples');
equal((3).upto(10, null, 3), [3,6,9], 'Number#upto | can handle multiples stops at 9');
equal((3).upto(8, null, 3), [3,6], 'Number#upto | can handle multiples stops at 8');
equal((9).downto(3, null, 3), [9,6,3], 'Number#downto | can handle multiples');
equal((9).downto(4, null, 3), [9,6], 'Number#downto | can handle multiples stops at 4');
equal((9).downto(2, null, 3), [9,6,3], 'Number#downto | can handle multiples stops at 2');
counter = 0;

@@ -245,3 +253,6 @@ (5).times(function(first) {

equal((0).pad(1, true), '+0', 'Number#pad | 0 padded to 1 place and sign')
equal((547.528).pad(4), '0547.528', 'Number#pad | does not take decimal places into account')
equal((255).pad(4, false, 16), '00ff', 'Number#pad | handles hex')
equal((2).pad(4, false, 2), '0010', 'Number#pad | handles binary')

@@ -253,5 +264,17 @@

equal((0.5).hex(), '0.8', 'Number#hex | 0.5')
equal((2.5).hex(), '2.8', 'Number#hex | 2.5')
equal((2.5).hex(), '2.8', 'Number#hex | 2.8')
equal((2553423).hex(), '26f64f', 'Number#hex | 2553423')
equal((0).hex(2), '00', 'Number#hex | padding 2 places | 0')
equal((10).hex(2), '0a', 'Number#hex | padding 2 places | 10')
equal((255).hex(2), 'ff', 'Number#hex | padding 2 places | 10')
equal((0.5).hex(2), '00.8', 'Number#hex | padding 2 places | 0.5')
equal((2.5).hex(2), '02.8', 'Number#hex | padding 2 places | 2.8')
equal((0).hex(4), '0000', 'Number#hex | padding 4 places | 0')
equal((10).hex(4), '000a', 'Number#hex | padding 4 places | 10')
equal((255).hex(4), '00ff', 'Number#hex | padding 4 places | 10')
equal((0.5).hex(4), '0000.8', 'Number#hex | padding 4 places | 0.5')
equal((2.5).hex(4), '0002.8', 'Number#hex | padding 4 places | 2.8')
equal((4).milliseconds(), 4, 'Number#milliseconds | 4');

@@ -258,0 +281,0 @@ equal((3.25).milliseconds(), 3, 'Number#milliseconds | rounded');

test('Object', function () {
var count,result;

@@ -226,3 +225,3 @@ var Person = function() {};

equal(Object.merge({ foo: 'bar' }, { broken: 'wear' }, { jumpy: 'jump' }, { fire: 'breath'}), { foo: 'bar', broken: 'wear', jumpy: 'jump', fire: 'breath' }, 'Object.merge | merge 3');
equal(Object.merge({ foo: 'bar' }, 'aha'), { foo: 'bar', 0: 'a', 1: 'h', 2: 'a' }, 'Object.merge | merge string', { mootools: { foo: 'bar', aha: undefined } });
equal(Object.merge({ foo: 'bar' }, 'aha'), { foo: 'bar' }, 'Object.merge | will not merge string', { mootools: { foo: 'bar', aha: undefined } });
equal(Object.merge({ foo: 'bar' }, null), { foo: 'bar' }, 'Object.merge | merge null');

@@ -235,3 +234,3 @@ equal(Object.merge({}, {}, {}), {}, 'Object.merge | merge multi empty');

equal(Object.merge({ foo: 'bar' }, 'wear', 8, null), { foo: 'bar', 0: 'w', 1: 'e', 2: 'a', 3: 'r' }, 'Object.merge | merge multi invalid', { mootools: { foo: 'bar', wear: 8 } });
equal(Object.merge({ foo: 'bar' }, 'wear', 8, null), { foo: 'bar' }, 'Object.merge | merge multi invalid', { mootools: { foo: 'bar', wear: 8 } });

@@ -242,7 +241,7 @@

equal(Object.extended({ foo: 'bar' }).merge({ broken: 'wear' }, { jumpy: 'jump' }, { fire: 'breath'}), { foo: 'bar', broken: 'wear', jumpy: 'jump', fire: 'breath' }, 'Object#merge | merge 3');
equal(Object.extended({ foo: 'bar' }).merge('aha'), { foo: 'bar', 0: 'a', 1: 'h', 2: 'a' }, 'Object#merge | merge string', { mootools: { foo: 'bar', aha: undefined } });
equal(Object.extended({ foo: 'bar' }).merge('aha'), { foo: 'bar' }, 'Object#merge | merge string', { mootools: { foo: 'bar', aha: undefined } });
equal(Object.extended({ foo: 'bar' }).merge(null), { foo: 'bar' }, 'Object#merge | merge null');
equal(Object.extended({}).merge({}, {}, {}), {}, 'Object#merge | merge multi empty');
equal(Object.extended({ foo: 'bar' }).merge('wear', 8, null), { foo: 'bar', 0: 'w', 1: 'e', 2: 'a', 3: 'r' }, 'Object#merge | merge multi invalid', { mootools: { foo: 'bar', wear: 8 } });
equal(Object.extended({ foo: 'bar' }).merge('wear', 8, null), { foo: 'bar' }, 'Object#merge | merge multi invalid', { mootools: { foo: 'bar', wear: 8 } });

@@ -255,3 +254,8 @@

var obj1 = {
var obj1, obj2, obj3;
obj1 = {
broken: 'wear',

@@ -265,7 +269,7 @@ foo: {

}
var obj2 = Object.clone(obj1);
obj2 = Object.clone(obj1);
equal(obj1.foo.jumpy, 'jump', 'Object.clone | cloned object has nested attribute');
obj1.foo.jumpy = 'hump';
equal(obj1.foo.jumpy, 'hump', 'Object.clone | original object is modified');
equal(obj2.foo.jumpy, 'jump', 'Object.clone | cloned object is not modified', { prototype: 'hump' });
equal(obj2.foo.jumpy, 'hump', 'Object.clone | clone is shallow', { mootools: 'jump' });

@@ -278,9 +282,13 @@ obj1 = {

obj2 = Object.clone(obj1);
obj3 = Object.clone(obj1, true);
obj1.foo.bar = ['a','b','c'];
equal(obj1.foo.bar, ['a','b','c'], 'Object#clone | original object is modified');
equal(obj2.foo.bar, [1,2,3], 'Object#clone | cloned object is not modified', { prototype: ['a','b','c'] });
equal(obj2.foo.bar, ['a','b','c'], 'Object#clone | clone is shallow', { mootools: [1,2,3] });
obj1.foo.bar = ['a','b','c'];
equal(obj3.foo.bar, [1,2,3], 'Object#clone | clone is deep', { prototype: ['a','b','c'] });
// Note here that the need for these complicated syntaxes is that both Prototype and Mootools' Object.clone is incorrectly

@@ -294,3 +302,5 @@ // cloning properties in the prototype chain directly into the object itself.

var obj1 = Object.extended({
var obj1, obj2, obj3;
obj1 = Object.extended({
broken: 'wear',

@@ -305,2 +315,3 @@ foo: {

obj2 = obj1.clone();
obj3 = obj1.clone(true);

@@ -310,3 +321,4 @@ equal(obj1.foo.jumpy, 'jump', 'Object#clone | cloned object has nested attribute');

equal(obj1.foo.jumpy, 'hump', 'Object#clone | original object is modified');
equal(obj2.foo.jumpy, 'jump', 'Object#clone | cloned object is not modified');
equal(obj2.foo.jumpy, 'hump', 'Object#clone | clone is shallow');
equal(obj3.foo.jumpy, 'jump', 'Object#clone | clone is deep');

@@ -321,10 +333,19 @@ equal(obj2.keys().sort(), ['broken','foo'], 'Object#clone | cloned objects are themselves extended');

obj2 = obj1.clone();
obj3 = obj1.clone(true);
obj1.foo.bar[1] = 'b';
equal(obj1.foo.bar, [1,'b',3], 'Object#clone | original object is modified');
equal(obj2.foo.bar, [1,2,3], 'Object#clone | cloned object is not modified');
equal(obj3.foo.bar, [1,2,3], 'Object#clone | cloned object is not modified');
equal(Object.isEmpty({}), true, 'Object.isEmpty | object is empty');
equal(Object.isEmpty({ broken: 'wear' }), false, 'Object.isEmpty | object is not empty');
equal(Object.isEmpty(null), true, 'Object.isEmpty | null is empty');
equal(Object.isEmpty(undefined), true, 'Object.isEmpty | undefined is empty');
equal(Object.isEmpty(''), true, 'Object.isEmpty | empty string is empty');
equal(Object.isEmpty(NaN), true, 'Object.isEmpty | NaN is empty');
equal(Object.isEmpty(0), true, 'Object.isEmpty | 0 is empty');
equal(Object.extended({}).isEmpty({}), true, 'Object#isEmpty | object is empty');

@@ -339,2 +360,5 @@ equal(Object.extended({ broken: 'wear' }).isEmpty(), false, 'Object#empty | object is not empty');

equal(Object.equals({x: 1, y: undefined}, {x: 1, z: 2}), false, 'Object.equals | undefined keys');
equal(Object.extended({ broken: 'wear' }).equals({ broken: 'wear' }), true, 'Object#equals | objects are equal');

@@ -426,3 +450,46 @@ equal(Object.extended({ broken: 'wear' }).equals({ broken: 'jumpy' }), false, 'Object#equals | objects are not equal');

// Object.fromQueryString
equal(Object.fromQueryString('foo=bar&moo=car'), {foo:'bar',moo:'car'}, 'String#fromQueryString | basic');
equal(Object.fromQueryString('foo=bar&moo=3'), {foo:'bar',moo:3}, 'String#fromQueryString | with numbers');
equal(Object.fromQueryString('foo=bar&moo=true'), {foo:'bar',moo:true}, 'String#fromQueryString | with true');
equal(Object.fromQueryString('foo=bar&moo=false'), {foo:'bar',moo:false}, 'String#fromQueryString | with false');
equal(Object.fromQueryString('foo=bar3'), { foo: 'bar3' }, 'String#fromQueryString | number in back');
equal(Object.fromQueryString('foo=3bar'), { foo: '3bar' }, 'String#fromQueryString | number up front');
equal(Object.fromQueryString('foo=345'), { foo: 345 }, 'String#fromQueryString | numbers only');
equal(Object.fromQueryString('foo=&bar='), { foo: '', bar: '' }, 'String#fromQueryString | undefined params');
equal(Object.fromQueryString('foo[]=bar&foo[]=car'), { foo: ['bar','car'] }, 'String#fromQueryString | handles array params');
equal(Object.fromQueryString('foo[bar]=tee&foo[car]=hee'), { foo: { bar: 'tee', car: 'hee' } }, 'String#fromQueryString | handles hash params');
equal(Object.fromQueryString('foo[0]=a&foo[1]=b&foo[2]=c'), { foo: ['a','b','c'] }, 'String#fromQueryString | handles array indexes');
equal(Object.fromQueryString('foo[cap][map]=3'), { foo: { cap: { map: 3 } } }, 'String#fromQueryString | handles array indexes');
equal(Object.fromQueryString('foo[cap][map][]=3'), { foo: { cap: { map: [3] } } }, 'String#fromQueryString | nested with trailing array');
equal(Object.fromQueryString('foo[moo]=1&bar[far]=2'), { foo: { moo: 1 }, bar: { far: 2 }}, 'String#fromQueryString | sister objects');
equal(Object.fromQueryString('f[]=a&f[]=b&f[]=c&f[]=d&f[]=e&f[]=f'), { f: ['a','b','c','d','e','f'] }, 'String#fromQueryString | large array');
equal(Object.fromQueryString('foo[0][]=a&foo[1][]=b'), { foo: [['a'],['b']] }, 'String#fromQueryString | nested arrays separate');
equal(Object.fromQueryString('foo[0][0]=3&foo[0][1]=4'), { foo: [[3,4]] }, 'String#fromQueryString | nested arrays together');
equal(Object.fromQueryString('foo[][]=3&foo[][]=4'), { foo: [[3],[4]] }, 'String#fromQueryString | nested arrays');
equal(Object.fromQueryString('foo[]=bar&foo[]=car', false), { 'foo[]': 'car' }, 'String#fromQueryString | disallows nested params');
var sparse = [];
sparse[3] = 'hardy';
sparse[10] = 'har har';
equal(Object.fromQueryString('foo[3]=hardy&foo[10]=har har'), { foo: sparse }, 'String#fromQueryString | constructed arrays can be sparse');
equal(Object.fromQueryString('text=What%20is%20going%20on%20here%3f%3f&url=http://animalsbeingdicks.com/page/2'), { text: 'What is going on here??', url: 'http://animalsbeingdicks.com/page/2' }, 'String#fromQueryString | handles escaped params');
equal(Object.fromQueryString('http://fake.com?foo=bar'), { foo: 'bar' }, 'String#fromQueryString | handles whole URLs');
equal(Object.fromQueryString('foo=bar&moo=car').keys(), ['foo', 'moo'], 'String#fromQueryString | should be extended');
equal(Object.fromQueryString(), {}, 'String#fromQueryString | will not die if no arguments');
if(typeof window !== 'undefined') {
equal(Object.isArray(Object.fromQueryString(window.location).keys()), true, 'String#fromQueryString | can handle just window.location');
}
});
// The scope when none is set.
nullScope = (function(){ return this; }).call();
var results;

@@ -6,16 +9,49 @@ var currentTest;

var syncTestsRunning = true;
var runningAsyncTests = 0;
// Capturing the timers here b/c Mootools (and maybe other frameworks) may clear a timeout that
// it kicked off after this script is loaded, which would throw off a simple incrementing mechanism.
var capturedTimers = [];
var nativeSetTimeout = setTimeout;
var nativeClearTimeout = clearTimeout;
// This declaration has the effect of pulling setTimeout/clearTimeout off the window's prototype
// object and setting it directly on the window itself. From here it can be globally reset while
// retaining the reference to the native setTimeout method. More about this here:
//
// http://www.adequatelygood.com/2011/4/Replacing-setTimeout-Globally
nullScope.setTimeout = nullScope.setTimeout;
nullScope.clearTimeout = nullScope.clearTimeout;
var nativeSetTimeout = nullScope.setTimeout;
var nativeClearTimeout = nullScope.clearTimeout;
var testStartTime;
var runtime;
// The scope when none is set.
nullScope = (function(){ return this; }).call();
var isInstanceOf = function(obj, str) {
return Object.prototype.toString.call(obj).toLowerCase() === '[object '+str+']';
};
// Arrays and objects must be treated separately here because in IE arrays with undefined
// elements will not pass the .hasOwnProperty check. For example [undefined].hasOwnProperty('0')
// will report false.
var deepEqual = function(one, two) {
if(one.length && two.length) {
return arrayEqual(one, two);
} else {
return objectEqual(one, two);
}
}
var arrayEqual = function(one, two) {
var i;
for(i = 0; i < one.length; i++) {
if(!isEqual(one[i], two[i])) {
return false;
}
}
return one.length === two.length;
}
var objectEqual = function(one, two) {
var onep = 0, twop = 0, key;

@@ -25,3 +61,3 @@ for(key in one) {

onep++;
if(typeof one[key] == 'object' && !deepEqual(one[key], two[key])) {
if(!isEqual(one[key], two[key])) {
return false;

@@ -37,2 +73,26 @@ }

var isEqual = function(one, two) {
if(one === nullScope && two === nullScope) {
// Null scope should always be a strict equal check
return true;
} else if(typeof two == 'number' && typeof one == 'number' && isNaN(two) && isNaN(one)) {
// NaN is NaN: equal
return true;
} else if(typeof two == 'object' && typeof one == 'object' && two != null && one != null && deepEqual(two, one)) {
// Deeply equal
return true;
} else if(two === null && one === null) {
// null is null: equal
return true;
} else if(two === undefined && one === undefined) {
// undefined is undefined: equal
return true;
} else if(isInstanceOf(one, typeof two) && two == one) {
// Strictly equal
return true;
} else {
return false;
}
}
var addFailure = function(actual, expected, message, stack, warning) {

@@ -48,3 +108,7 @@ var meta = getMeta(stack);

}
var s = new Error().stack.split('\n');
var e = new Error();
if(!e.stack) {
return {};
}
var s = e.stack.split('\n');
var match = s[level].match(/\/(.+?):(\d+)(?:(\d+))?/);

@@ -55,3 +119,3 @@ return { file: match[1], line: match[2] };

var checkCanFinish = function() {
if(!syncTestsRunning && runningAsyncTests == 0) {
if(!syncTestsRunning && capturedTimers.length == 0) {
testsFinished();

@@ -114,6 +178,5 @@ }

setTimeout = function(fn, delay) {
runningAsyncTests++;
var timer = nativeSetTimeout(function() {
fn.apply(this, arguments);
runningAsyncTests--;
removeCapturedTimer(timer);
checkCanFinish();

@@ -126,12 +189,13 @@ }, delay);

clearTimeout = function(timer) {
removeCapturedTimer(timer);
return nativeClearTimeout(timer);
}
var removeCapturedTimer = function(timer) {
var index = capturedTimers.indexOf(timer);
if(index != -1) {
if(index !== -1) {
capturedTimers.splice(index, 1);
runningAsyncTests--;
}
return nativeClearTimeout.apply(this, arguments);
}
};
nativeTimeoutReturnType = typeof setTimeout(function(){}, 0);
equal = function(actual, expected, message, exceptions, stack) {

@@ -143,11 +207,3 @@ exceptions = exceptions || {};

currentTest.assertions++;
if(actual === nullScope && expected === nullScope) {
// Null scope should always be a strict equal check
} else if(typeof expected == 'number' && typeof actual == 'number' && isNaN(expected) && isNaN(actual)) {
// NaN == NaN: equal
} else if(typeof expected == 'object' && typeof actual == 'object' && deepEqual(expected, actual)) {
// Deeply equal
} else if(expected == actual) {
// Strictly equal
} else {
if(!isEqual(actual, expected)) {
addFailure(actual, expected, message, stack);

@@ -161,2 +217,6 @@ }

notEqual = function(actual, expected, message, exceptions) {
equal(actual !== expected, true, message + ' | strict equality', exceptions, 1);
}
equalWithWarning = function(expected, actual, message) {

@@ -183,3 +243,3 @@ if(expected != actual) {

skipEnvironments = function(environments, test) {
if(!environments.has(environment)) {
if(environments.indexOf(environment) === -1) {
test.call();

@@ -186,0 +246,0 @@ }

@@ -8,5 +8,5 @@ (function($) {

var env = $('#' + environment);
results.each(function(module) {
results.forEach(function(module) {
var mod = $('<ul class="module" />');
module.results.each(function(r) {
module.results.forEach(function(r) {
totalTests++;

@@ -18,6 +18,10 @@ totalAssertions += r.assertions;

if(r.failures.length > 0) {
r.failures.each(function(f) {
r.failures.forEach(function(f) {
title += getFailureHTML(f);
if(f.warning) {
totalFailed--;
}
});
var warning = r.failures.all(function(f){ return f.warning; });
var warning = r.failures.every(function(f){ return f.warning; });
if(warning) {

@@ -54,12 +58,36 @@ li.addClass('warning');

$('[title]', env).tooltip({ color: 'black' });
$(document).trigger('tests_finished', [environment]);
}
var getFailureHTML = function(f) {
var expected, actual;
if(f.warning) {
return '<p class="warning">Warning: ' + f.message + '</p>';
} else {
return '<p class="fail">' + f.message + ', expected: ' + f.expected + ' actual: ' + f.actual + '</p>';
expected = getStringified(f.expected);
actual = getStringified(f.actual);
return '<p class="fail">' + f.message + ', expected: ' + expected + ' actual: ' + actual + '</p>';
}
};
var getStringified = function(p) {
if(typeof JSON !== 'undefined' && JSON.stringify) return JSON.stringify(p);
if(typeof p !== 'object') return String(p);
var isArray = p.join;
var str = isArray ? '[' : '{';
var arr;
arr = [];
for(var key in p){
if(!p.hasOwnProperty(key)) continue;
if(p[key] === undefined) {
arr.push('undefined');
} else {
arr.push(p[key]);
}
}
str += arr.join(',');
str += isArray ? ']' : '}';
return str;
};
})(jQuery);

@@ -132,3 +132,3 @@ (function($){

function animateTooltip(s, options, el, fn){
var color = getDefault('color', options, el, 'white');
var color = getDefault('color', options, el, 'black');
var duration = getDefault('duration', options, el, 250);

@@ -135,0 +135,0 @@ tooltip.attr('class', color + ' ' + s.direction);

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc