master
1(function(context) {
2
3
4 // BEGIN LIBS
5// Compatiblity index:
6//
7// 0 - Does not exist.
8// 1 - Exists but does not support all functionality.
9// 2 - Exists and supports all functionality.
10// 3 - Exists and supports all functionality plus more.
11
12
13var SugarPrototypeMethods = [
14 {
15 // Global namespace
16 type: 'class',
17 namespace: 'Global',
18 methods: [
19 {
20 name: 'Hash',
21 description: 'Creates a hash object with methods specific to working with hashes.',
22 sugar_compatibility: 1,
23 sugar_notes: 'The Hash class does not exist in Sugar, however hash-like objects exist through Object.extended, which will return an extended object with instance methods on it similar to hashes in Prototype. Keep in mind, however, that the instance methods available to extended objects in Sugar do not 100% match those of Prototype.',
24 original_code: "$H({ 0: 'a', 1: 'b', 2: 'c' })",
25 sugar_code: "Object.extended({ 0: 'a', 1: 'b', 2: 'c' })",
26 ref: 'Object/extended'
27 },
28 {
29 name: '$A',
30 description: 'Creates an array from an array-like collection',
31 sugar_compatibility: 1,
32 sugar_notes: '$A exists in Sugar as Array.create. Be aware, however, that when a Javascript primitive is passed, it will simply be added to the array. If, for example, you need a string to be split into the array, then use the standard String#split instead. The most common use of this method, converting an arguments object into an actual array, will work, however.',
33 original_code: '$A(arguments)',
34 sugar_code: 'Array.create(arguments)',
35 ref: 'Array/create'
36 },
37 {
38 name: '$H',
39 description: 'Creates a hash object with methods specific to working with hashes.',
40 sugar_compatibility: 1,
41 sugar_notes: '$H exists in Sugar as Object.extended. This will return an extended object with instance methods on it similar to hashes in Prototype. Keep in mind, however, that the instance methods available to extended objects in Sugar do not 100% match those of Prototype.',
42 original_code: "$H({ 0: 'a', 1: 'b', 2: 'c' })",
43 sugar_code: "Object.extended({ 0: 'a', 1: 'b', 2: 'c' })",
44 ref: 'Object/extended'
45 },
46 {
47 name: '$R',
48 description: 'Creates an ObjectRange object that represents a range of consecutive values.',
49 sugar_compatibility: 0,
50 sugar_notes: '$R exists in Sugar as ranges, which can be created via Number.range, Date.range, or String.range. Additionally Number#upto or Number#downto will return an array of numbers in that range.',
51 original_code: "$R(0, 10)",
52 sugar_code: "(0).upto(10)",
53 ref: 'Number/upto'
54 },
55 {
56 name: '$w',
57 description: 'Splits a string into an array with all whitespace treated as a delimiter',
58 sugar_compatibility: 0,
59 sugar_notes: '$w does not exist in Sugar. Use native String#split instead.',
60 original_code: "$w('apples and oranges')",
61 sugar_code: "'apples and oranges'.split(' ')"
62 }
63 ]
64},
65{
66 namespace: 'Number',
67 type: 'instance',
68 methods: [
69 {
70 name: 'succ',
71 description: 'Returns the successor (+1) of the current number.',
72 sugar_compatibility: 0,
73 sugar_notes: 'Number#succ does not exist in Sugar. This is often done with the + operator :)',
74 original_code: "num.succ()",
75 sugar_code: "num + 1"
76 },
77 {
78 name: 'times',
79 description: 'Calls the passed iterator n times, where n is the number.',
80 sugar_compatibility: 1,
81 sugar_notes: 'Number#times exists in Sugar but does not take a context argument. Use Function#bind for this purpose instead.',
82 original_code: "(8).times(function(){}, 'barf')",
83 sugar_code: "(8).times(function(){}.bind('barf'))",
84 live_notes: 'Number#times does not accept a second argument, but found {1}. If you need to bind context, use Function#bind instead.',
85 conflict: function() {
86 var type = typeof arguments[1];
87 return [arguments.length > 1 && type != 'number', arguments[1]];
88 },
89 ref: 'Function/bind'
90 },
91 {
92 name: 'toColorPart',
93 description: 'Returns a 2 digit hexadecimal representation of the number.',
94 sugar_compatibility: 2,
95 sugar_notes: 'Number#toColorPart exists in Sugar as Number#hex (pad to 2 places).',
96 original_code: "(255).toColorPart()",
97 sugar_code: "(255).hex(2)",
98 ref: 'Number/hex'
99 },
100 {
101 name: 'toPaddedString',
102 description: 'Returns a string representation of the number padded with zeros.',
103 sugar_compatibility: 2,
104 sugar_notes: 'Number#toPaddedString exists in Sugar as Number#pad. Note that in Sugar, the radix is the third argument, as the second is to force the sign.',
105 original_code: "(20).toPaddedString(4, 2)",
106 sugar_code: "(20).pad(4, false, 2)",
107 ref: 'Number/pad'
108 },
109 {
110 name: 'abs',
111 description: 'Returns the absolute value of the number.',
112 sugar_compatibility: 2,
113 sugar_notes: 'Number#abs exists in Sugar and is identical.',
114 conflict: false,
115 ref: 'Number/abs'
116 },
117 {
118 name: 'ceil',
119 description: 'Rounds then number up.',
120 sugar_compatibility: 3,
121 sugar_notes: 'Number#ceil exists in Sugar. It can additionally round to a precision, specified in the first argument.',
122 conflict: false,
123 ref: 'Number/ceil'
124 },
125 {
126 name: 'floor',
127 description: 'Rounds then number down.',
128 sugar_compatibility: 3,
129 sugar_notes: 'Number#floor exists in Sugar. It can additionally round to a precision, specified in the first argument.',
130 conflict: false,
131 ref: 'Number/floor'
132 },
133 {
134 name: 'round',
135 description: 'Rounds then number.',
136 sugar_compatibility: 3,
137 sugar_notes: 'Number#round exists in Sugar. It can additionally round to a precision, specified in the first argument.',
138 conflict: false,
139 ref: 'Number/round'
140 }
141 ]
142},
143{
144 namespace: 'String',
145 type: 'class',
146 methods: [
147 {
148 name: 'interpret',
149 description: 'Coerces the value into a string.',
150 sugar_compatibility: 0,
151 sugar_notes: 'String.interpret does not exist in Sugar. Use String() instead. Note, however that this will not convert null/undefined into a blank string, as is the case with String.interpret.',
152 original_code: "String.interpret(156)",
153 sugar_code: "String(156)"
154 }
155 ]
156},
157{
158 namespace: 'String',
159 type: 'instance',
160 methods: [
161 {
162 name: 'blank',
163 description: 'Checks if the string is empty or contains only whitespace.',
164 sugar_compatibility: 2,
165 sugar_notes: 'String#blank exists in Sugar as String#isBlank.',
166 original_code: "'hello'.blank()",
167 sugar_code: "'hello'.isBlank()",
168 ref: 'String/isBlank'
169 },
170 {
171 name: 'camelize',
172 description: 'Converts a string to its camelCase equivalent.',
173 sugar_compatibility: 3,
174 sugar_notes: 'String#camelize exists and will also operate on whitespace and underscores as well as dashes.',
175 live_notes: 'String#camelize found white space or underscores! Note that #camelize in Sugar will remove whitespace and operate on underscores the same as dashes.',
176 conflict: function() {
177 return /[ _]/.test(this);
178 },
179 ref: 'String/camelize'
180 },
181 {
182 name: 'dasherize',
183 description: 'Replaces underscores with dashes.',
184 sugar_compatibility: 3,
185 sugar_notes: 'Sugar#dasherize will also remove whitespace and convert camelCase as well.',
186 conflict: function() {
187 return /[A-Z\s]/.test(this);
188 },
189 live_notes: 'String#dasherize found white space or capital letters! Note that #dasherize in Sugar will remove whitespace and operate on capital letters the same as underscores.',
190 ref: 'String/dasherize'
191 },
192 {
193 name: 'empty',
194 description: 'Checks if the string is empty.',
195 sugar_compatibility: 0,
196 sugar_notes: 'String#empty does not exist in Sugar. Use a straight comparison instead.',
197 original_code: "str.empty()",
198 sugar_code: "str === ''"
199 },
200 {
201 name: 'evalJSON',
202 description: 'Evaluates the string as JSON and returns the resulting object.',
203 sugar_compatibility: 0,
204 sugar_notes: 'String#evalJSON does not exist in Sugar. JSON.parse may work as an alternative, but it is not available in all browsers. If you absolutely require JSON support, consider adding in a separate library such as: https://github.com/douglascrockford/JSON-js',
205 original_code: "str.evalJSON()",
206 sugar_code: "JSON.parse(str)"
207 },
208 {
209 name: 'evalScripts',
210 description: 'Evaluates the content of any <script> block in the string.',
211 sugar_compatibility: 0,
212 sugar_notes: "String#evalScripts does not exist in Sugar. It's highly unlikely that you should be doing something like this anyway, (and even if you are, libraries like jQuery should perform this automatically), but if you really need to in a pinch, something like this may work:",
213 original_code: "str.evalScripts()",
214 sugar_code: "str.match(/<script.*?>.+?<\/script>/g).map(function(m){\n return eval(m.replace(/<\\/?script.*?>/g, ''));\n})"
215 },
216 {
217 name: 'extractScripts',
218 description: 'Extracts <script> blocks in the string and returns them as an array.',
219 sugar_compatibility: 0,
220 sugar_notes: 'String#extractScripts does not exist in Sugar. If you really need to do this, then in a pinch something like this may work.',
221 original_code: "str.extractScripts()",
222 sugar_code: "str.match(/<script.*?>.+?<\/script>/g).map(function(m){\n return m.replace(/<\\/?script.*?>/g, '');\n})"
223 },
224 {
225 name: 'gsub',
226 description: 'Returns the string with every occurrence of the passed regex replaced.',
227 sugar_compatibility: 0,
228 sugar_notes: 'String#gsub does not exist in Sugar. Just use the native .replace function instead. Note that Javascript allows functions to be passed to .replace just like gsub.',
229 original_code: "'Image: (img.png)'.gsub(/Image: \(.+\)/, function(match, src) {\n return '<img src=\"' + src + '\" />';\n})",
230 sugar_code: "'Image: (img.png)'.replace(/Image: \(.+\)/, function(match, src) {\n return '<img src=\"' + src + '\" />';\n})"
231 },
232 {
233 name: 'include',
234 description: 'Checks if the string contains the substring.',
235 sugar_compatibility: 2,
236 sugar_notes: 'String#include exists in Sugar as String#has.',
237 original_code: "'foobar'.include('bar')",
238 sugar_code: "'foobar'.has('bar')",
239 ref: 'String/has'
240 },
241 {
242 name: 'inspect',
243 description: 'Returns a debug oriented version of the string.',
244 sugar_compatibility: 0,
245 sugar_notes: 'String#inspect does not exist in Sugar. Consider using JSON.stringify(str) instead. The JSON global does not exist in all implementations but should be enough to get you through a debug session.',
246 original_code: "'foofa'.inspect()",
247 sugar_code: "JSON.stringify('foofa')"
248 },
249 {
250 name: 'interpolate',
251 description: 'Fills the string with properties of an object, using the syntax #{}.',
252 sugar_compatibility: 3,
253 sugar_notes: 'String#interpolate exists in Sugar as String#assign, with a slightly different syntax.',
254 original_code: "'i like #{fruit}'.interpolate({ fruit: 'peaches' })",
255 sugar_code: "'i like {fruit}'.assign({ fruit: 'peaches' })",
256 ref: 'String/assign'
257 },
258 {
259 name: 'isJSON',
260 description: 'Checks if the string is valid JSON.',
261 sugar_compatibility: 0,
262 sugar_notes: 'String#isJSON does not exist in Sugar. The simplest way of determining if a value is JSON or not is to attempt parsing and catch errors. If you absolutely require full JSON support, consider adding in a separate library such as: https://github.com/douglascrockford/JSON-js',
263 original_code: "valid = str.isJSON()",
264 sugar_code: "try { JSON.parse(str); valid = true; } catch(e){ valid = false; }"
265 },
266 {
267 name: 'scan',
268 description: 'Iterates over every occurrence of the passed regex pattern.',
269 sugar_compatibility: 3,
270 sugar_notes: 'String#scan exists in Sugar as String#each. It additionally returns an array of the matched patterns.',
271 original_code: "'apple, pear & orange'.scan(/\w+/, console.log)",
272 sugar_code: "'apple, pear & orange'.each(/\w+/, console.log)",
273 ref: 'String/each'
274 },
275 {
276 name: 'strip',
277 description: 'Removes leading and trailing whitespace from the string.',
278 sugar_compatibility: 2,
279 sugar_notes: 'String#strip exists in Sugar as String#trim. This is an ES5 standard method that Sugar provides a shim for when unavailable.',
280 original_code: "' howdy '.strip()",
281 sugar_code: "' howdy '.trim()",
282 ref: 'String/trim'
283 },
284 {
285 name: 'stripScripts',
286 description: 'Strips HTML <script> tags from the string.',
287 sugar_compatibility: 3,
288 sugar_notes: 'String#stripScripts can be achieved in Sugar with String#removeTags.',
289 original_code: "'<script>doEvilStuff();</script>'.stripScripts()",
290 sugar_code: "'<script>doEvilStuff();</script>'.removeTags('script')",
291 ref: 'String/removeTags'
292 },
293 {
294 name: 'stripTags',
295 description: 'Strips the string of any HTML tags.',
296 sugar_notes: 'String#stripTags exists in Sugar and will additionally strip tags like <xsl:template>.',
297 sugar_compatibility: 3,
298 live_notes: 'String#stripTags found namespaced tags such as <xsl:template>. Be aware that Sugar will strip these tags too!',
299 conflict: function() {
300 return arguments.length == 0 && /<.*?:.*?>/.test(this);
301 },
302 ref: 'String/stripTags'
303 },
304 {
305 name: 'sub',
306 description: 'Returns a string with the first occurrence of the regex pattern replaced.',
307 sugar_compatibility: 0,
308 sugar_notes: 'String#sub does not exist in Sugar. Standard Javascript .replace is the closest approximation. If 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.',
309 original_code: "'one two three four'.sub(' ', ', ', 2)",
310 sugar_code: "var c = 0; 'one two three four'.replace(/\s/g, function(m, i){ c++; return c < 3 ? ', ' : ' '; })"
311 },
312 {
313 name: 'succ',
314 description: 'Returns the next character in the Unicode table.',
315 sugar_compatibility: 3,
316 sugar_notes: '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.',
317 original_code: "'a'.succ()",
318 sugar_code: "'a'.shift(1);",
319 ref: 'String/shift'
320 },
321 {
322 name: 'times',
323 description: 'Concatenates the string n times, where n is the first argument.',
324 sugar_compatibility: 2,
325 sugar_notes: 'String#times exists in Sugar as String#repeat.',
326 original_code: "'echo '.times(3)",
327 sugar_code: "'echo '.repeat(3);",
328 ref: 'String/repeat'
329 },
330 {
331 name: 'toArray',
332 description: 'Returns the string as an array of characters.',
333 sugar_compatibility: 3,
334 sugar_notes: 'String#toArray exists in Sugar as String#chars, which can also run a function against each character.',
335 original_code: "'howdy'.toArray()",
336 sugar_code: "'howdy'.chars();",
337 ref: 'String/chars'
338 },
339 {
340 name: 'toQueryParams',
341 description: 'Parses a URI-like query string and returns an object of key/value pairs.',
342 sugar_compatibility: 3,
343 sugar_notes: 'String#toQueryParams exists in Sugar but from an inverted perspective as Object.fromQueryString. Note that by default this will also parse out nested params with the non-standard "[]" syntax, however this can be turned off.',
344 original_code: "'section=blog&id=45'.toQueryParams()",
345 sugar_code: "Object.fromQueryString('section=blog&id=45')",
346 ref: 'Object/fromQueryString'
347 },
348 {
349 name: 'truncate',
350 description: 'Truncates a string to the given length and adds a suffix to it.',
351 sugar_compatibility: 3,
352 sugar_notes: 'String#truncate exists in Sugar and additionally allows truncating from the left or middle. Also, String#truncateOnWord will do the same as truncate but not split words.',
353 original_code: "longString.truncate(10)",
354 sugar_code: "longString.truncate(10)",
355 ref: 'String/truncate'
356 },
357 {
358 name: 'underscore',
359 description: 'Converts a camelized string into words separated by an underscore.',
360 sugar_compatibility: 3,
361 sugar_notes: 'String#underscore exists in Sugar and will additionally remove all white space.',
362 conflict: function() {
363 return /\s/.test(this);
364 },
365 live_notes: 'String#underscore found white space! Note that underscore in Sugar will remove all whitespace.',
366 ref: 'String/underscore'
367 },
368 {
369 name: 'unfilterJSON',
370 description: 'Strips comment delimiters around Ajax JSON or Javascript responses.',
371 sugar_compatibility: 0,
372 sugar_notes: 'String#unfilterJSON does not exist in Sugar.'
373 },
374 {
375 name: 'capitalize',
376 description: 'Capitalizes the first letter of the string and downcases the others.',
377 sugar_compatibility: 3,
378 sugar_notes: 'String#capitalize exists in Sugar. Passing true as the first argument will additionally capitalize all words.',
379 conflict: false,
380 ref: 'String/capitalize'
381 },
382 {
383 name: 'startsWith',
384 description: 'Checks if the string starts with the passed substring.',
385 sugar_compatibility: 3,
386 sugar_notes: 'String#startsWith exists in Sugar and additionally accepts an argument for case sensitivity (default is true).',
387 conflict: false,
388 ref: 'String/startsWith'
389 },
390 {
391 name: 'endsWith',
392 description: 'Checks if the string ends with the passed substring.',
393 sugar_compatibility: 3,
394 sugar_notes: 'String#endsWith exists in Sugar and additionally accepts an argument for case sensitivity (default is true).',
395 conflict: false,
396 ref: 'String/endsWith'
397 },
398 {
399 name: 'escapeHTML',
400 description: 'Converts HTML special characters to their entity equivalents.',
401 sugar_compatibility: 2,
402 sugar_notes: 'String#escapeHTML exists in Sugar is identical.',
403 conflict: false,
404 ref: 'String/escapeHTML'
405 },
406 {
407 name: 'unescapeHTML',
408 description: 'Converts HTML entities to their normal form.',
409 sugar_compatibility: 2,
410 sugar_notes: 'String#unescapeHTML exists in Sugar is identical.',
411 ref: 'String/unescapeHTML'
412 }
413 ]
414},
415{
416 namespace: 'Hash',
417 type: 'instance',
418 methods: [
419 {
420 name: 'each',
421 description: 'Iterates over each key/value entry in the hash.',
422 sugar_notes: 'Extended objects in Sugar function like hashes and have an identical each method.',
423 sugar_compatibility: 2,
424 conflict: function() {
425 var type = typeof arguments[1];
426 return [arguments.length > 1 && type != 'number', arguments[1]];
427 },
428 live_notes: 'Second argument to .each found. If you need to bind context, use Function#bind instead.',
429 original_code: "new Hash().each(function(){}, 'context')",
430 sugar_code: "Object.extended().each(function(){}.bind('context'))",
431 ref: 'Object/each'
432 },
433 {
434 name: 'get',
435 description: 'Retrieves the hash value for the given key.',
436 sugar_notes: 'Sugar extended objects do not have a "get" method. Simply access the property as you would a normal object literal.',
437 sugar_compatibility: 0,
438 original_code: "var h = new Hash({ foo: 'bar' }); h.get('foo')",
439 sugar_code: "var h = Object.extended({ foo: 'bar' }); h['foo']",
440 ref: 'Object/extended'
441 },
442 {
443 name: 'index',
444 description: 'Returns the first key in the hash whose value matches the passed value.',
445 sugar_compatibility: 0,
446 sugar_notes: 'Sugar extended objects do not have an "index" method. Object.keys can provide help to get this.',
447 original_code: "var key = new Hash({ foo: 'bar' }).index('bar')",
448 sugar_code: "var key; Object.extended({ foo: 'bar' }).keys(function(k){ if(this[k] == 'bar') key = k; })"
449 },
450 {
451 name: 'inspect',
452 description: 'Returns a debug-oriented string representation of the hash.',
453 sugar_compatibility: 0,
454 sugar_notes: 'Sugar extended objects do not have an "inspect" method. Consider using JSON.stringify() instead. The JSON global does not exist in all implementations but should be enough to get you through a debug session.',
455 original_code: "new Hash({ foo: 'bar' }).inspect()",
456 sugar_code: "JSON.stringify(Object.extended({ foo: 'bar' }))"
457 },
458 {
459 name: 'merge',
460 description: "Returns a new hash with the passed object's key/value pairs merged in.",
461 sugar_compatibility: 3,
462 sugar_notes: 'Sugar extended objects have a "merge" method, and have additional functionality such as specifying deep/shallow merges and resolution of conflicts. Unlike prototype they will modify the object. If you need to create a clone, use the "clone" method first.',
463 original_code: "new Hash({ foo: 'bar' }).merge({ moo: 'car' })",
464 sugar_code: "Object.extended({ foo: 'bar' }).clone().merge({ moo: 'car' })",
465 ref: 'Object/merge'
466 },
467 {
468 name: 'set',
469 description: 'Sets a value in the hash for the given key.',
470 sugar_compatibility: 0,
471 sugar_notes: 'Sugar extended objects do not have a "set" method. Simply set the property as you would a normal object literal.',
472 original_code: "var h = new Hash({ foo: 'bar' }); h.set('moo', 'car')",
473 sugar_code: "var h = Object.extended({ foo: 'bar' }); h['moo'] = 'car'",
474 ref: 'Object/extended'
475 },
476 {
477 name: 'toJSON',
478 description: 'Returns a JSON representation of the hash.',
479 sugar_compatibility: 0,
480 sugar_notes: 'Sugar extended objects do not have a "toJSON" method. JSON.stringify may work as an alternative, but it is not available in all browsers. If you absolutely require JSON support, consider adding in a separate library such as: https://github.com/douglascrockford/JSON-js',
481 original_code: "Object.toJSON(obj)",
482 sugar_code: "JSON.stringify(obj)"
483 },
484 {
485 name: 'toObject',
486 description: 'Returns a cloned, vanilla object with the same properties as the hash.',
487 sugar_compatibility: 0,
488 sugar_notes: 'Sugar extended objects do not have a "toObject" method, as they already behave like vanilla objects.',
489 ref: 'Object/extended'
490 },
491 {
492 name: 'toTemplateReplacements',
493 description: 'Returns a vanilla object, alias of Hash#toObject.',
494 sugar_compatibility: 0,
495 sugar_notes: 'Sugar extended objects do not have a "toTemplateReplacements" method. This method is not necessary as extended objects already behave like vanilla objects.',
496 ref: 'Object/extended'
497 },
498 {
499 name: 'unset',
500 description: 'Deletes the property in the hash for the given key.',
501 sugar_compatibility: 0,
502 sugar_notes: 'Sugar extended objects do not have an "unset" method. Simply delete the property as you would a normal object literal.',
503 original_code: "var h = new Hash({ foo: 'bar' }); h.unset('foo')",
504 sugar_code: "var h = Object.extended({ foo: 'bar' }); delete h.foo",
505 ref: 'Object/extended'
506 },
507 {
508 name: 'update',
509 description: 'Updates the hash in place by merging in the passed object.',
510 sugar_compatibility: 2,
511 sugar_notes: 'Hash#update exists in Sugar as "merge" on extended objects.',
512 original_code: "new Hash({ foo: 'bar' }).merge({ moo: 'car' })",
513 sugar_code: "Object.extended({ foo: 'bar' }).merge({ moo: 'car' })",
514 ref: 'Object/merge'
515 },
516 {
517 name: 'clone',
518 description: 'Returns a clone of the hash.',
519 sugar_compatibility: 2,
520 sugar_notes: 'Hash#clone exists on Sugar extended objects, and is identical.',
521 original_code: "new Hash({ foo: 'bar' }).clone()",
522 sugar_code: "Object.extended({ foo: 'bar' }).clone()",
523 conflict: false,
524 ref: 'Object/clone'
525 },
526 {
527 name: 'keys',
528 description: 'Returns an array of all keys defined on the hash.',
529 sugar_compatibility: 2,
530 sugar_notes: 'Hash#keys exists on Sugar extended objects, and is identical.',
531 original_code: "new Hash({ foo: 'bar' }).keys()",
532 sugar_code: "Object.extended({ foo: 'bar' }).keys()",
533 conflict: false,
534 ref: 'Object/keys'
535 },
536 {
537 name: 'values',
538 description: 'Returns an array of all values in the hash.',
539 sugar_compatibility: 2,
540 sugar_notes: 'Hash#values exists on Sugar extended objects, and is identical.',
541 original_code: "new Hash({ foo: 'bar' }).values()",
542 sugar_code: "Object.extended({ foo: 'bar' }).values()",
543 conflict: false,
544 ref: 'Object/values'
545 },
546 {
547 name: 'toQueryString',
548 description: 'Returns a URL-encoded string representing the contents of the hash.',
549 sugar_compatibility: 0,
550 sugar_notes: 'Hash#toQueryString exists in Sugar as Object.toQueryString.',
551 original_code: "hash.toQueryString()",
552 sugar_code: "Object.toQueryString(obj);"
553 }
554 ]
555},
556{
557 namespace: 'Object',
558 type: 'class',
559 methods: [
560 {
561 name: 'clone',
562 description: 'Returns a shallow copy of the object.',
563 sugar_compatibility: 3,
564 sugar_notes: 'Object.clone exists in Sugar and additionally can create deep clones as well.',
565 conflict: false,
566 ref: 'Object/clone'
567 },
568 {
569 name: 'extend',
570 description: 'Copies all properties from the source to the destination object.',
571 sugar_compatibility: 3,
572 sugar_notes: 'Object.extend exists in Sugar as Object.merge. It can optionally do deep merges as well as intelligent conflict resolution.',
573 original_code: "Object.extend({ a: 1 }, { b: 2 })",
574 sugar_code: "Object.merge({ a: 1 }, { b: 2 })",
575 ref: 'Object/merge'
576 },
577 {
578 name: 'inspect',
579 description: 'Returns a debug-oriented representation of the object.',
580 sugar_compatibility: 0,
581 sugar_notes: 'Object.inspect does not exist in Sugar. Consider using JSON.stringify(object) instead. The JSON global does not exist in all implementations but should be enough to get you through a debug session.',
582 original_code: "Object.inspect([1,2,3])",
583 sugar_code: "JSON.stringify([1,2,3])"
584 },
585 {
586 name: 'isHash',
587 description: 'Returns true if the object is a hash.',
588 sugar_compatibility: 2,
589 sugar_notes: 'Object.isHash does not exist in Sugar. Use Object.isObject instead.',
590 original_code: "Object.isHash({ a: 1 })",
591 sugar_code: "Object.isObject({ a: 1 })",
592 ref: 'Object/isObject'
593 },
594 {
595 name: 'isUndefined',
596 description: 'Returns true if the object is undefined.',
597 sugar_compatibility: 0,
598 sugar_notes: 'Object.isUndefined does not exist in Sugar. Use straight Javascript instead.',
599 original_code: "Object.isUndefined(obj)",
600 sugar_code: "obj === undefined"
601 },
602 {
603 name: 'toJSON',
604 description: 'Returns a JSON representation of the object.',
605 sugar_compatibility: 0,
606 sugar_notes: 'Object.toJSON does not exist in Sugar. JSON.stringify may work as an alternative, but it is not available in all browsers. If you absolutely require JSON support, consider adding in a separate library such as: https://github.com/douglascrockford/JSON-js',
607 original_code: "Object.toJSON(obj)",
608 sugar_code: "JSON.stringify(obj)"
609 },
610 {
611 name: 'isArray',
612 description: 'Returns true if the object is an array.',
613 sugar_compatibility: 2,
614 sugar_notes: 'Object.isArray and is an alias of browser native Array.isArray, which is shimmed if it does not exist.',
615 conflict: false,
616 ref: 'Object/isArray'
617 },
618 {
619 name: 'isDate',
620 description: 'Returns true if the object is a date.',
621 sugar_compatibility: 2,
622 sugar_notes: 'Object.isDate exists in Sugar and is identical.',
623 conflict: false,
624 ref: 'Object/isDate'
625 },
626 {
627 name: 'isFunction',
628 description: 'Returns true if the object is a function.',
629 sugar_compatibility: 2,
630 sugar_notes: 'Object.isFunction exists in Sugar and is identical.',
631 conflict: false,
632 ref: 'Object/isFunction'
633 },
634 {
635 name: 'isNumber',
636 description: 'Returns true if the object is a number.',
637 sugar_compatibility: 2,
638 sugar_notes: 'Object.isNumber exists in Sugar and is identical.',
639 conflict: false,
640 ref: 'Object/isNumber'
641 },
642 {
643 name: 'isString',
644 description: 'Returns true if the object is a string.',
645 sugar_compatibility: 2,
646 sugar_notes: 'Object.isString exists in Sugar and is identical.',
647 conflict: false,
648 ref: 'Object/isString'
649 },
650 {
651 name: 'keys',
652 description: 'Returns an array of the keys in the object.',
653 sugar_compatibility: 2,
654 sugar_notes: 'Object.keys is a browser native method, which Sugar provides a shim for if it does not exist.',
655 conflict: false,
656 ref: 'Object/keys'
657 },
658 {
659 name: 'values',
660 description: 'Returns an array of the values in the object.',
661 sugar_compatibility: 2,
662 sugar_notes: 'Object.values exists in Sugar and is identical.',
663 conflict: false,
664 ref: 'Object/values'
665 },
666 {
667 name: 'isElement',
668 description: 'Returns true if the object is a DOM element.',
669 sugar_compatibility: 0,
670 sugar_notes: "Sugar, which has no direct association with the DOM, does not provide this method. However, this functionality can be easily replicated (taken from Prototype's own implementation).",
671 original_code: 'Object.isElement(obj)',
672 sugar_code: 'Object.extend({ isElement: function(obj){ return !!(obj && obj.nodeType == 1); }}, false, false);'
673 },
674 {
675 name: 'toHTML',
676 description: 'Returns an HTML representation of the object.',
677 sugar_compatibility: 0,
678 sugar_notes: "Object.toHTML does not exist in Sugar. You'll have to define this one yourself!"
679 },
680 {
681 name: 'toQueryString',
682 description: 'Returns a URL-encoded string representing the contents of the object.',
683 sugar_compatibility: 0,
684 sugar_notes: 'Object.toQueryString exists in Sugar and additionally allows a parameter to namespace deep params.',
685 original_code: "Object.toQueryString(obj)",
686 sugar_code: "Object.toQueryString(obj);"
687 }
688 ]
689},
690{
691 namespace: 'Array',
692 type: 'class',
693 methods: [
694 {
695 name: 'from',
696 description: 'Creates an array from an array-like collection.',
697 sugar_compatibility: 2,
698 sugar_notes: 'Array.from exists in Sugar as Array.create. Be aware, however, that when a Javascript primitive is passed, it will simply be added to the array. If, for example, you need a string to be split into the array, then use the standard String#split instead. The most common use of this method, converting an arguments object into an actual array, will work, however.',
699 original_code: "Array.from(arguments)",
700 sugar_code: "Array.create(arguments)",
701 ref: 'Array.create'
702 }
703 ]
704},
705{
706 namespace: 'Array',
707 type: 'instance',
708 methods: [
709 {
710 name: 'collect',
711 description: 'Returns the result of applying an iterator to the array.',
712 sugar_compatibility: 3,
713 sugar_notes: 'Enumerable#collect exists Sugar as Array#map, and can additionally pass a shortcut string for a function that returns a property of the same name.',
714 original_code: "[1,2,3].collect(function(n){ return n * 2; })",
715 sugar_code: "[1,2,3].map(function(n){ return n * 2; })",
716 ref: 'Array/map'
717 },
718 {
719 name: 'detect',
720 description: 'Returns the first element for which the iterator returns a truthy value.',
721 sugar_compatibility: 3,
722 sugar_notes: 'Enumerable#detect exists in Sugar as Array#find, and is identical.',
723 original_code: "[1,2,3].detect(function(n){ return n > 1; })",
724 sugar_code: "[1,2,3].find(function(n){ return n > 1; })",
725 ref: 'Array/find'
726 },
727 {
728 name: 'each',
729 description: 'Iterates over the key/value pairs in the hash.',
730 sugar_compatibility: 3,
731 sugar_notes: 'Array#each exists in Sugar and can additionally pass a starting index and loop from the beginning of the array. If context needs to be bound, use Function#bind instead.',
732 conflict: function() {
733 var type = typeof arguments[1];
734 return [arguments.length > 1 && type != 'number', arguments[1]];
735 },
736 live_notes: 'Second argument to .each should be a number but instead was {1}. If you need to bind context, use Function#bind instead.',
737 original_code: "['a','b','c'].each(function(){}, 'context')",
738 sugar_code: "['a','b','c'].each(function(){}.bind('context'))",
739 ref: 'Array/each'
740 },
741 {
742 name: 'eachSlice',
743 description: 'Groups array elements into chunks of a given size and iterates over them.',
744 sugar_compatibility: 2,
745 sugar_notes: 'Enumerable#eachSlice exists in Sugar as Array#inGroupsOf instead.',
746 original_code: "[1,2,3,4].eachSlice(2, function(){})",
747 sugar_code: "[1,2,3,4].inGroupsOf(2).each(function(){})",
748 ref: 'Array/inGroupsOf'
749 },
750 {
751 name: 'entries',
752 description: 'alias of enumerable#toarray.',
753 sugar_compatibility: 2,
754 sugar_notes: 'Enumerable#entries is not necessary in Sugar, but its behavior of effectively cloning the array can be achieved with Array#clone.',
755 original_code: "[1,2,3].entries()",
756 sugar_code: "[1,2,3].clone()",
757 ref: 'Array/clone'
758 },
759 {
760 name: 'find',
761 description: 'Returns the first element for which the iterator returns a truthy value.',
762 sugar_notes: 'Array#find also exists in Sugar and is identical.',
763 sugar_compatibility: 3,
764 conflict: function() {
765 var type = typeof arguments[1];
766 return [arguments.length > 1 && type != 'number', arguments[1]];
767 },
768 live_notes: 'Second argument to Array#find should be a number but instead was {1}. If you need to bind context, use Function#bind instead.',
769 original_code: "['a','b','c'].find(function(){}, 'context')",
770 sugar_code: "['a','b','c'].find(function(){}.bind('context'))",
771 ref: 'Function/bind'
772 },
773 {
774 name: 'findAll',
775 description: 'Returns all elements for which the iterator returns a truthy value.',
776 sugar_notes: 'Array#findAll also exists in Sugar. Some semantic differences exist including the ability to pass a starting index in the place of in-line context binding.',
777 sugar_compatibility: 3,
778 conflict: function() {
779 var type = typeof arguments[1];
780 return [arguments.length > 1 && type != 'number', arguments[1]];
781 },
782 live_notes: 'Second argument to Array#findAll should be a number but instead was {1}. If you need to bind context, use Function#bind instead.',
783 original_code: "['a','b','c'].findAll(function(){}, 'context')",
784 sugar_code: "['a','b','c'].findAll(function(){}.bind('context'))",
785 ref: 'Function/bind'
786 },
787 {
788 name: 'grep',
789 description: 'Returns all elements for which the passed regex matches.',
790 sugar_compatibility: 3,
791 sugar_notes: 'Enumerable#grep exists in Sugar as Array#findAll with slightly different semantics.',
792 original_code: "['a','b','c'].grep(/[ab]/)",
793 sugar_code: "['a','b','c'].findAll(/[ab]/)",
794 ref: 'Array/findAll'
795 },
796 {
797 name: 'include',
798 description: 'Returns true if the array contains the given element.',
799 sugar_compatibility: 3,
800 sugar_notes: 'Enumerable#include exists in Javascript as native Array#some, or the Sugar alias Array#any. Array#include in Sugar instead the passed argument to the array without modifying it. Array#include is a reciprocal of Array#exclude, and a non-destructive version of Array#add.',
801 conflict: function(f) {
802 return typeof f !== 'object' && arguments.length == 1;
803 },
804 original_code: "[1,2,3].include(1)",
805 sugar_code: "[1,2,3].any(1)",
806 ref: 'Array/has'
807 },
808 {
809 name: 'inject',
810 description: 'Incrementally builds a return value by successively iterating over the array.',
811 sugar_compatibility: 2,
812 sugar_notes: 'Enumerable#inject in Javascript as native Array#reduce. Sugar provides a shim for this method when it does not exist.',
813 original_code: '[1,2,3,4].inject(100, function(a, b){ return a + b; });',
814 sugar_code: '[1,2,3,4].reduce(function(a, b){ return a + b; }, 100);'
815 },
816 {
817 name: 'invoke',
818 description: 'Invokes the same method for each element in the array.',
819 sugar_compatibility: 2,
820 sugar_notes: 'Sugar allows a string shortcut to be passed to Array#map, which achieves the same effect as Array#invoke.',
821 original_code: "['hello','world'].invoke('toUpperCase')",
822 sugar_code: "['hello','world'].map('toUpperCase')",
823 ref: 'Array/map'
824 },
825 {
826 name: 'max',
827 description: 'Returns the element of the array with the highest value.',
828 sugar_compatibility: 2,
829 sugar_notes: 'Array#max exists in Sugar and additionally has the ability to return all the maximum values, as more than one may exist. Sugar also returns the actual array element instead of the return value of the iterator. Sugar also does not allow a context parameter, use Function#bind instead.',
830 live_notes: 'Use caution when using Enumerable#max: (1) Sugar will return an array of maximum values (as there can be more than one), where Prototype only returns the first value. (2) When using iterators, Prototype will return the value compared, where Sugar will return the actual array element itself. (3) Finally, Sugar does not allow a context to be passed. Use Function#bind instead to bind context.',
831 original_code: "[{ a: 5 },{ a: 10 }].max(function(el){ return el['a']; }, 'context')",
832 sugar_code: "[{ a: 5 },{ a: 10 }].max(function(el){ return el['a']; }.bind('context')).first().a",
833 ref: 'Array/max'
834 },
835 {
836 name: 'member',
837 description: 'Returns true if the array contains the given element. Alias of Enumerable#include.',
838 sugar_compatibility: 2,
839 sugar_notes: 'Enumerable#member exists in Javascript as Array#some or the Sugar alias Array#any.',
840 original_code: "[1,2,3].member(1)",
841 sugar_code: "[1,2,3].any(1)",
842 ref: 'Array/has'
843 },
844 {
845 name: 'min',
846 description: 'Returns the element of the array with the lowest value.',
847 sugar_compatibility: 2,
848 sugar_notes: 'Array#min exists in Sugar and additionally has the ability to return all the minimum values, as more than one may exist. Sugar also returns the actual array element instead of the return value of the iterator. Sugar also does not allow a context parameter, use Function#bind instead.',
849 live_notes: 'Use caution when using Enumerable#min: (1) Sugar will return an array of minimum values (as there can be more than one), where Prototype only returns the first value. (2) When using iterators, Prototype will return the value compared, where Sugar will return the actual array element itself. (3) Finally, Sugar does not allow a context to be passed.',
850 original_code: "[{ a: 5 },{ a: 10 }].min(function(el){ return el['a']; }, 'context')",
851 sugar_code: "[{ a: 5 },{ a: 10 }].min(function(el){ return el['a']; }.bind('context')).first().a",
852 ref: 'Array/min'
853 },
854 {
855 name: 'partition',
856 description: 'Partitions the array into two groups, one for which the iterator passed returns true, and one for which the iterator returns false.',
857 sugar_compatibility: 3,
858 sugar_notes: "Enumerable#partition does not exist in Sugar. Array#groupBy however has similar functionality, and may be a suitable alternative. It will create a hash with keys based on the return values of the iterator, with each grouping as the value. Instead of accessing the split array, you can access the hash by these keys. This method has the added advantage that it can also split into more than two groups.",
859 original_code: "[1,2,3,4,5,6].partition(function(n){ return n % 2 === 0; })",
860 sugar_code: "[1,2,3,4,5,6].group(function(n){ return n % 2 === 0 ? 'even' : 'odd'; })",
861 ref: 'Array/groupBy'
862 },
863 {
864 name: 'pluck',
865 description: 'Returns a mapped array of the same property of each element in the array.',
866 sugar_compatibility: 2,
867 sugar_notes: 'Sugar allows a string shortcut to Array#map, making it effectively identical to Enumerable#pluck.',
868 original_code: "['hello','world'].pluck('length')",
869 sugar_code: "['hello','world'].map('length')",
870 ref: 'Array/map'
871 },
872 {
873 name: 'reject',
874 description: 'Returns all elements for which the iterator returns a falsy value.',
875 sugar_compatibility: 3,
876 sugar_notes: "Enumerable#reject does not exist in Sugar. Its equivalent is Array#exclude. This is a non-destructive way to remove elements from an array. If you want a destructive version, use Array#remove instead. Also note these methods' reciprocals: Array#include and Array#add.",
877 original_code: "[1,2,3].reject(function(n){ n < 3; })",
878 sugar_code: "[1,2,3].exclude(function(n){ n < 3; })",
879 ref: 'Array/exclude'
880 },
881 {
882 name: 'select',
883 description: 'Returns all elements for which the iterator returns a truthy value. Alias of Enumerable#findAll.',
884 sugar_compatibility: 3,
885 sugar_notes: 'Enumerable#select exists in Sugar as Array#findAll.',
886 original_code: "[1,2,3].select(function(n){ n < 3; })",
887 sugar_code: "[1,2,3].findAll(function(n){ n < 3; })",
888 ref: 'Array/findAll'
889 },
890 {
891 name: 'sortBy',
892 description: 'Returns an array sorted on the value returned by the iterator.',
893 sugar_compatibility: 3,
894 sugar_notes: 'Array#sortBy in Sugar additionally allows a flag for descending order and also has a mechanism for intelligent alphanumeric sorting of string properties. For binding of context, use Function#bind instead.',
895 conflict: function(f, scope) {
896 var type = typeof arguments[1];
897 return [arguments.length > 1 && type != 'boolean', arguments[1]];
898 },
899 live_notes: 'Second argument to .sortBy should be a boolean but instead was {1}. If you need to bind context, use Function#bind instead.',
900 original_code: "[{ a: 5 },{ a: 10 }].sortBy(function(el){ return el['a']; })",
901 sugar_code: "[{ a: 5 },{ a: 10 }].sortBy(function(el){ return el['a']; }.bind('context'))",
902 ref: 'Function/bind'
903 },
904 {
905 name: 'size',
906 description: 'Returns the length of the array.',
907 sugar_compatibility: 2,
908 sugar_notes: 'Enumerable#size does not exist in Sugar. Just use array.length!',
909 original_code: "[1,2,3].size()",
910 sugar_code: "[1,2,3].length"
911 },
912 {
913 name: 'toArray',
914 description: 'Returns a clone of the array.',
915 sugar_compatibility: 2,
916 sugar_notes: 'Enumerable#toArray does not exist in Sugar. Use Array#clone instead.',
917 original_code: "[1,2,3].toArray()",
918 sugar_code: "[1,2,3].clone()",
919 ref: 'Array/clone'
920 },
921 {
922 name: 'zip',
923 description: '"zips" together multiple arrays into a single multi-dimensional array.',
924 sugar_compatibility: 2,
925 sugar_notes: 'Enumerable#zip exists in Sugar and is identical.',
926 original_code: "firstNames.zip(lastNames)",
927 sugar_code: "firstNames.zip(lastNames)",
928 ref: 'Array/zip'
929 },
930 {
931 name: 'compact',
932 description: 'Returns a copy of the array with all undefined and null values removed.',
933 sugar_compatibility: 3,
934 sugar_notes: 'Array#compact exists and is nearly identical except that it will also remove any values which are NaN from the array as well. It additionally has a flag to remove all falsy values.',
935 conflict: function() {
936 for(var i = 0; i < this.length; i++){
937 if(isNaN(this[i])) return true;
938 }
939 return false;
940 },
941 live_notes: 'Caution: Array#compact was called on an array that contains NaN values. Sugar will remove these from the array while Prototype leaves them alone.',
942 ref: 'Array/compact'
943 },
944 {
945 name: 'clear',
946 description: 'Removes all elements from the array.',
947 sugar_compatibility: 0,
948 sugar_notes: 'Array#clear does not exist in Sugar. Use array.length = 0 or simply set array = [] instead.',
949 original_code: "f.clear()",
950 sugar_code: "f = []"
951 },
952 {
953 name: 'inspect',
954 description: 'Returns a debug-oriented string representing the array.',
955 sugar_compatibility: 0,
956 sugar_notes: 'Array#inspect does not exist in Sugar. Consider using JSON.stringify(array) instead. The JSON global does not exist in all implementations but should be enough to get you through a debug session.',
957 original_code: "[1,2,3].inspect()",
958 sugar_code: "JSON.stringify([1,2,3])"
959 },
960 {
961 name: 'reverse',
962 description: 'Reverses the arrays contents.',
963 sugar_compatibility: 2,
964 conflict: function(inline) {
965 return inline === false;
966 },
967 sugar_notes: 'Array#reverse exists in native Javascript, but is destructive. For a non-destructive version use Array#clone first.',
968 original_code: "array.reverse(false)",
969 sugar_code: "array.clone().reverse()",
970 ref: 'Array.clone'
971 },
972 {
973 name: 'uniq',
974 description: 'Returns a new array without duplicates.',
975 sugar_compatibility: 3,
976 sugar_notes: 'Array#uniq exists in Sugar as Array#unique and can also operate on arrays of objects. Additionally it accepts a mapping function to indicate the field to uniquify on.',
977 original_code: "[1,1,1].uniq()",
978 sugar_code: "[1,1,1].unique()",
979 ref: 'Array/unique'
980 },
981 {
982 name: 'without',
983 description: 'Creates a new array that does not contain the specified values.',
984 sugar_compatibility: 3,
985 sugar_notes: 'Array#without exists in Sugar as Array#exclude.',
986 original_code: "[1,2,3].without(3)",
987 sugar_code: "[1,2,3].exclude(3)",
988 ref: 'Array/exclude'
989 },
990 {
991 name: 'indexOf',
992 description: 'Returns the index of the first occurence of the item in the array.',
993 sugar_compatibility: 2,
994 sugar_notes: 'Array#indexOf exists natively in modern browsing engines. Sugar provides this method when it is not supported.',
995 conflict: false,
996 ref: 'Array/indexOf'
997 },
998 {
999 name: 'lastIndexOf',
1000 description: 'Returns the index of the last occurence of the item in the array.',
1001 sugar_compatibility: 2,
1002 sugar_notes: 'Array#lastIndexOf exists natively in modern browsing engines. Sugar provides this method when it is not supported.',
1003 conflict: false,
1004 ref: 'Array/lastIndexOf'
1005 },
1006 {
1007 name: 'all',
1008 description: 'Returns true if the passed iterator returns a truthy value for all elements in the array.',
1009 sugar_compatibility: 3,
1010 sugar_notes: 'Array#all exists in Sugar as an alias to native Array#every (for which it adds a shim for browsers without support), and is identical.',
1011 conflict: false,
1012 ref: 'Array/all'
1013 },
1014 {
1015 name: 'any',
1016 description: 'Returns true if the passed iterator returns a truthy value for any elements in the array.',
1017 sugar_compatibility: 2,
1018 sugar_notes: 'Array#any exists in Sugar as an alias to native Array#some (for which it adds a shim for browsers without support).',
1019 conflict: false,
1020 ref: 'Array/any'
1021 },
1022 {
1023 name: 'map',
1024 description: 'Returns the result of applying an iterator to the array. Alias of Enumerable#collect.',
1025 sugar_compatibility: 2,
1026 sugar_notes: 'Array#map exists natively in modern browsing engines. Sugar provides this method when it is not supported, and additionally augments it to handle string shortcuts.',
1027 conflict: false,
1028 ref: 'Array/map'
1029 },
1030 {
1031 name: 'every',
1032 description: 'Returns true if the passed iterator returns a truthy value for all elements in the array.',
1033 sugar_compatibility: 3,
1034 sugar_notes: 'Array#every exists natively in modern browsing engines. Sugar provides this method when it is not supported, and additionally augments it to handle strings, numbers, regexes, and deep objects.',
1035 conflict: false,
1036 ref: 'Array/every'
1037 },
1038 {
1039 name: 'filter',
1040 description: 'Returns all elements for which the iterator returns a truthy value.',
1041 sugar_compatibility: 3,
1042 sugar_notes: 'Array#filter exists natively in modern browsing engines. Sugar provides this method when it is not supported, and additionally augments it to handle strings, numbers, regexes, and deep objects.',
1043 conflict: false,
1044 ref: 'Array/filter'
1045 },
1046 {
1047 name: 'some',
1048 description: 'Returns true if the passed iterator returns a truthy value for any elements in the array.',
1049 sugar_compatibility: 3,
1050 sugar_notes: 'Array#some exists natively in modern browsing engines. Sugar provides this method when it is not supported, and additionally augments it to handle strings, numbers, regexes, and deep objects.',
1051 conflict: false,
1052 ref: 'Array/some'
1053 },
1054 {
1055 name: 'inGroupsOf',
1056 description: 'Splits the array into groups of n elements, where n is the number passed.',
1057 sugar_compatibility: 2,
1058 sugar_notes: 'Array#inGroupsOf exists in Sugar and is identical.',
1059 conflict: false,
1060 ref: 'Array/inGroupsOf'
1061 },
1062 {
1063 name: 'clone',
1064 description: 'Returns a copy of the array.',
1065 sugar_compatibility: 2,
1066 sugar_notes: 'Array#clone exists in Sugar and is identical.',
1067 conflict: false,
1068 ref: 'Array/clone'
1069 },
1070 {
1071 name: 'first',
1072 description: 'Returns the first element in the array.',
1073 sugar_compatibility: 3,
1074 sugar_notes: 'Array#first exists in Sugar, and can additionally get the first n elements.',
1075 conflict: false,
1076 ref: 'Array/first'
1077 },
1078 {
1079 name: 'last',
1080 description: 'Returns the last element in the array.',
1081 sugar_compatibility: 3,
1082 sugar_notes: 'Array#last exists in Sugar, and can additionally get the last n elements.',
1083 conflict: false,
1084 ref: 'Array/last'
1085 },
1086 {
1087 name: 'flatten',
1088 description: 'Returns a one-dimensional copy of the array.',
1089 sugar_compatibility: 3,
1090 sugar_notes: 'Array#flatten exists in Sugar, and can additionally flatten an array to any level.',
1091 conflict: false,
1092 ref: 'Array/flatten'
1093 },
1094 {
1095 name: 'intersect',
1096 description: 'Returns an array containing every item that that is shared between the passed arrays.',
1097 sugar_compatibility: 3,
1098 sugar_notes: 'Array#intersect exists in Sugar, and can additionally get the intersection of any number of arrays passed in.',
1099 conflict: false,
1100 ref: 'Array/intersect'
1101 }
1102 ]
1103},
1104{
1105 namespace: 'Function',
1106 type: 'instance',
1107 methods: [
1108 {
1109 name: 'bind',
1110 description: 'Binds the function to the given context.',
1111 sugar_compatibility: 2,
1112 sugar_notes: 'Function#bind exists natively in modern browsing engines. Sugar provides this method when it is not supported.',
1113 conflict: false,
1114 ref: 'Function/bind'
1115 },
1116 {
1117 name: 'argumentNames',
1118 description: 'Returns an array of the argument names as stated in the function definition.',
1119 sugar_compatibility: 0,
1120 sugar_notes: 'Function#argumentNames does not exist in Sugar.'
1121
1122 },
1123 {
1124 name: 'bindAsEventListener',
1125 description: 'An event specific version Function#bind. Will allow an event object to be passed as the first argument to the bound function.',
1126 sugar_compatibility: 0,
1127 sugar_notes: 'Function#bindAsEventListener does not exist in Sugar, but can be easily approximated with an undefined space for the event in the arguments using Function#fill. Keep in mind, however, that this is only necessary if you are also currying arguments as well. If you are just trying to bind context, then Function#bind alone is enough.',
1128 original_code: "(function(event, one) { this == \"bound\", one == 1; }).bindAsEventListener('bound', 1) ",
1129 sugar_code: "(function(event, one) { this == \"bound\", one == 1; }).fill('bound', undefined, 1)",
1130 ref: 'Function/bind'
1131 },
1132 {
1133 name: 'curry',
1134 description: 'Burns-in arguments to a function and returns a new function.',
1135 sugar_compatibility: 3,
1136 sugar_notes: 'Function#curry exists in Sugar as Function#fill. When passing undefined to Function#fill it will additionally serve as a placeholder where arguments to the original function will be allowed in.',
1137 original_code: "fn.curry('one','two')",
1138 sugar_code: "fn.fill('one', 'two')",
1139 ref: 'Function/fill'
1140 },
1141 {
1142 name: 'defer',
1143 description: 'Schedules the function to run as soon as the interpreter is idle.',
1144 sugar_compatibility: 2,
1145 sugar_notes: 'Function#defer exists in Sugar as Function#delay. When no params are passed it will behave precisely the same as calling the function with a timeout of 1 ms (as with defer).',
1146 original_code: "fn.defer()",
1147 sugar_code: "fn.delay()",
1148 ref: 'Function/delay'
1149 },
1150 {
1151 name: 'delay',
1152 description: 'Schedules the function to run after a specified amount of time.',
1153 sugar_compatibility: 1,
1154 sugar_notes: 'Function#delay exists in Sugar, but is slightly different. First, the delay is passed in milliseconds, not seconds. Second, delay will return a reference to the function instead of an integer to clear the timeout. If you need to cancel the timeout, instead use Function#cancel. Arguments passed after the timeout are still curried like Prototype.',
1155 original_code: "var t = fn.delay(2) clearTimeout(t) ",
1156 sugar_code: "fn.delay(2000) fn.cancel()",
1157 ref: 'Function/delay'
1158 },
1159 {
1160 name: 'methodize',
1161 description: 'Wraps the function inside another function that pushes the object it is called on as the first argument.',
1162 sugar_compatibility: 0,
1163 sugar_notes: 'Function#methodize does not exist in Sugar. No direct equivalent exists, but in a pinch the following code will achieve the same effect.',
1164 original_code: "obj.method = fn.methodize()",
1165 sugar_code: "obj.method = function(){ fn.apply(null, [this].concat(Array.prototype.slice.call(arguments))); }"
1166 },
1167 {
1168 name: 'wrap',
1169 description: 'Returns a function wrapped around the original function.',
1170 sugar_compatibility: 0,
1171 sugar_notes: 'Function#wrap does not exist in Sugar. No direct equivalent exists, but Function#bind can be used to achieve the same effect in a pinch.',
1172 original_code: "fn = fn.wrap(function(original){ return original() + 3; })",
1173 sugar_code: "fn = (function(original){ return original() + 3; }).bind(null, fn);"
1174 }
1175 ]
1176},
1177{
1178 namespace: 'Date',
1179 type: 'instance',
1180 methods: [
1181 {
1182 name: 'toISOString',
1183 description: 'Returns an ISO8601 representation of the date.',
1184 sugar_compatibility: 2,
1185 sugar_notes: 'Date#toISOString exists natively in modern browsing engines. Sugar provides this method when it is not supported.',
1186 conflict: false,
1187 ref: 'Date/toISOString'
1188 },
1189 {
1190 name: 'toJSON',
1191 description: 'Returns an ISO8601 representation of the date, identical to Date#toISOString.',
1192 sugar_compatibility: 2,
1193 sugar_notes: 'Date#toJSON exists natively in modern browsing engines. Sugar provides this method when it is not supported.',
1194 conflict: false,
1195 ref: 'Date/toJSON'
1196 }
1197 ]
1198}
1199];
1200
1201
1202// Compatiblity index:
1203//
1204// 0 - Does not exist.
1205// 1 - Exists but does not support all functionality.
1206// 2 - Exists and supports all functionality.
1207// 3 - Exists and supports all functionality plus more.
1208
1209var SugarUnderscoreMethods = [
1210 {
1211 type: 'class',
1212 namespace: '_',
1213 methods: [
1214 {
1215 name: 'each',
1216 description: 'Iterates over an enumerable collection.',
1217 sugar_compatibility: 3,
1218 sugar_notes: '_.each exists natively on arrays in modern browsing engines as Array#forEach. Sugar provides this method when it is not supported. It also provides Array#each, which can break out of the loop, start from a given index, loop from the beginning, and intelligently handle sparse arrays. Sugar also provides Object.each for iterating over objects.',
1219 original_code: '_.each([1,2,3], function(element, index, array){})',
1220 sugar_code: '[1,2,3].forEach(function(element, index, array){})',
1221 ref: 'Array/each'
1222 },
1223 {
1224 name: 'map',
1225 description: 'Creates a new array by using the return value of a mapping function on each element.',
1226 sugar_compatibility: 3,
1227 sugar_notes: '_.map exists natively in modern browsing engines as Array#map. Sugar provides this method when it is not supported, and additionally augments it to allow passing a string as a shortcut.',
1228 original_code: '_.map([1,2,3], function(a){ return a * 2; })',
1229 sugar_code: '[1,2,3].map(function(a){ return a * 2; })',
1230 ref: 'Array/map'
1231 },
1232 {
1233 name: 'reduce',
1234 description: 'Boils down a list of values to a single value, optionally with a starting value.',
1235 sugar_compatibility: 2,
1236 sugar_notes: '_.reduce exists natively in modern browsing engines as Array#reduce. Sugar provides this method when it is not supported.',
1237 original_code: '_.reduce([1,2,3], function(m, a){ return m + a; })',
1238 sugar_code: '[1,2,3].reduce(function(m, a){ return m + a; })',
1239 ref: 'Array/reduce'
1240 },
1241 {
1242 name: 'reduceRight',
1243 description: 'Boils down a list of values to a single value starting from the last entry (the right), optionally with a starting value.',
1244 sugar_compatibility: 2,
1245 sugar_notes: '_.reduceRight exists natively in modern browsing engines as Array#reduceRight. Sugar provides this method when it is not supported.',
1246 original_code: '_.reduceRight([1,2,3], function(m, a){ return m + a; })',
1247 sugar_code: '[1,2,3].reduceRight(function(m, a){ return m + a; })',
1248 ref: 'Array/reduceRight'
1249 },
1250 {
1251 name: 'find',
1252 description: 'Finds the first value in the list for which the iterator returns true.',
1253 sugar_compatibility: 3,
1254 sugar_notes: '_.find exists in Sugar as Array#find, and additionally allows searching on primitives, deep objects, and testing against regexes.',
1255 original_code: '_.find([1,2,3], function(a){ return a == 1; })',
1256 sugar_code: '[1,2,3].find(1)',
1257 bind_context: true,
1258 ref: 'Array/find'
1259 },
1260 {
1261 name: 'filter',
1262 description: 'Finds all values in the list for which the iterator returns true.',
1263 sugar_compatibility: 3,
1264 sugar_notes: '_.filter exists natively in modern browsing engines as Array#filter. Sugar provides this method when it is not supported, and additionally augments it to allow search on primitives, deep objects, or against a regex. Sugar also provides Array#findAll which can start from a given index, loop from the beginning, and intelligently handles sparse arrays.',
1265 original_code: '_.filter([1,2,3], function(a){ return a % 2 == 0; })',
1266 sugar_code: '[1,2,3].reduceRight(function(a){ return a % 2 == 0; })',
1267 ref: 'Array/filter'
1268 },
1269 {
1270 name: 'reject',
1271 description: 'Returns all elements in the list for which the iterator does not return true. Opposite of filter.',
1272 sugar_compatibility: 3,
1273 sugar_notes: '_.reject exists in Sugar as Array#exclude. It additionally allows searching on primitives, deep objects, or against a regex. This method is non-destructive with a destructive reciprocal method: Array#remove.',
1274 original_code: '_.reject([1,2,3,4,5,6], function(a){ return a % 2 == 0; })',
1275 sugar_code: '[1,2,3,4,5,6].exclude(function(a){ return a % 2 == 0; })',
1276 ref: 'Array/exclude'
1277 },
1278 {
1279 name: 'all',
1280 description: 'Returns true if the iterator returns true for all elements in the list, false otherwise.',
1281 sugar_compatibility: 3,
1282 sugar_notes: '_.all exists natively in modern browsing engines as Array#every. Sugar provides this method when it is not supported, and addtionally augments it to search on primitives, deep objects, or against a regex. Sugar also has its own alias Array#all.',
1283 original_code: '_.all([1,2,3], function(a){ return a == 2; })',
1284 sugar_code: '[1,2,3].all(function(a){ return a % 2 == 0; })',
1285 ref: 'Array/all'
1286 },
1287 {
1288 name: 'any',
1289 description: 'Returns true if the iterator returns true for any elements in the list, false otherwise.',
1290 sugar_compatibility: 3,
1291 sugar_notes: '_.any exists natively in modern browsing engines as Array#some. Sugar provides this method when it is not supported, and addtionally augments it to search on primitives, deep objects, or against a regex. Sugar also has its own alias Array#any.',
1292 original_code: '_.any([1,2,3], function(a){ return a % 2 == 0; })',
1293 sugar_code: '[1,2,3].any(function(a){ return a % 2 == 0; })',
1294 ref: 'Array/any'
1295 },
1296 {
1297 name: 'include',
1298 description: 'Returns true if the value is present in the list.',
1299 sugar_compatibility: 3,
1300 sugar_notes: '_.include exists in Sugar as browser native Array#some, which it augments to search on primitive types, deep objects, or against regexes. Sugar also has its own alias Array#any, which has identical functionality.',
1301 original_code: '_.include([1,2,3], 3)',
1302 sugar_code: '[1,2,3].any(3)',
1303 ref: 'Array/any'
1304 },
1305 {
1306 name: 'invoke',
1307 description: 'Calls the passed method name for each value in the list.',
1308 sugar_compatibility: 2,
1309 sugar_notes: '_.invoke does not exist in Sugar. In most cases, invoking functions through standard methods is more readable. Array#map effectively provides an alias for this, however.',
1310 original_code: "_.invoke([5,1,7],[3,2,1], 'sort')",
1311 sugar_code: "[[5,1,7],[3,2,1]].map('sort')",
1312 ref: 'Array/map'
1313 },
1314 {
1315 name: 'pluck',
1316 description: 'Returns the property for each value in the list.',
1317 sugar_compatibility: 2,
1318 sugar_notes: '_.pluck exists in Sugar as browser native Array#map, which it augments to accept a string as a shortcut.',
1319 original_code: "_.pluck([{name:'moe'},{name:'larry'},{name:'curly'}], 'name')",
1320 sugar_code: "[{name:'moe'},{name:'larry'},{name:'curly'}].map('name')",
1321 ref: 'Array/map'
1322 },
1323 {
1324 name: 'max',
1325 description: 'Returns the maximum value in the list.',
1326 sugar_compatibility: 2,
1327 sugar_notes: '_.max exists in Sugar as Array#max, and can additionally return an array when more than one max values exist.',
1328 original_code: '_.max([1,2,3])',
1329 sugar_code: '[1,2,3].max()',
1330 ref: 'Array/max'
1331 },
1332 {
1333 name: 'min',
1334 description: 'Returns the minimum value in the list.',
1335 sugar_compatibility: 2,
1336 sugar_notes: '_.min exists in Sugar as Array#min, and can additionally return an array when more than one max values exist.',
1337 original_code: '_.min([1,2,3])',
1338 sugar_code: '[1,2,3].min()',
1339 ref: 'Array/min'
1340 },
1341 {
1342 name: 'sortBy',
1343 description: 'Returns a copy of the list sorted by the result of the iterator.',
1344 sugar_compatibility: 2,
1345 sugar_notes: '_.sortBy exists in Sugar as Array#sortBy. In addition to an iterating function, it will also accept a string as a shortcut to the property to sort by.',
1346 original_code: '_.sortBy([1,2,3], Math.sin)',
1347 sugar_code: '[1,2,3].sortBy(Math.sin)',
1348 ref: 'Array/sortBy'
1349 },
1350 {
1351 name: 'groupBy',
1352 description: 'Splits a collection into sets, grouping them by the result of running each value through the iterator.',
1353 sugar_compatibility: 3,
1354 sugar_notes: '_.groupBy exists in Sugar as Array#groupBy. It allows passing a string as a shortcut to a property and additionally allows an optional callback to be called for each group.',
1355 original_code: '_.groupBy([1,2,3,4], function(n){ return n > 2; })',
1356 sugar_code: '[1,2,3,4].groupBy(function(n){ return n > 2; })',
1357 ref: 'Array/groupBy'
1358 },
1359 {
1360 name: 'sortedIndex',
1361 description: 'Determine the index at which the value should be inserted into the list in order to maintain the sorted order.',
1362 sugar_compatibility: 0,
1363 sugar_notes: '_.sortedIndex does not exist in Sugar. Clever use of Array#reduce can achieve something similar, depending on the case.',
1364 original_code: '_.sortedIndex([1,2,3,5], 4)',
1365 sugar_code: '[1,2,3,5].reduce(function(a, b, i){ if(b > 4) return i - 1; })',
1366 ref: 'Array/reduce'
1367 },
1368 {
1369 name: 'shuffle',
1370 description: 'Returns a randomized copy of the list.',
1371 sugar_compatibility: 2,
1372 sugar_notes: '_.shuffle exists in Sugar as Array#randomize. Sugar also uses a Fisher-Yates algorithm.',
1373 original_code: '_.shuffle([1,2,3,4])',
1374 sugar_code: '[1,2,3,4].randomize()',
1375 ref: 'Array/randomize'
1376 },
1377 {
1378 name: 'toArray',
1379 description: 'Converts any enumerable object into an array.',
1380 sugar_compatibility: 3,
1381 sugar_notes: '_.toArray exists in Sugar as Array.create, which can accept multiple arguments.',
1382 original_code: '_.toArray(arguments)',
1383 sugar_code: 'Array.create(arguments)',
1384 ref: 'Array/create'
1385 },
1386 {
1387 name: 'size',
1388 description: 'Returns the number of values in the list.',
1389 sugar_compatibility: 0,
1390 sugar_notes: "_.size does not exist in Sugar. If you need to know the \"size\" of a hash, you can get the length of Object.keys, although in that case it's likely that you should be using an array in any case.",
1391 original_code: '_.size(obj)',
1392 sugar_code: 'Object.keys(obj).length',
1393 ref: 'Object/keys'
1394 },
1395 {
1396 name: 'first',
1397 description: 'Returns the first element(s) of the array.',
1398 sugar_compatibility: 2,
1399 sugar_notes: '_.first exists in Sugar as Array#first, and is identical.',
1400 original_code: '_.first([1,2,3])',
1401 sugar_code: '[1,2,3].first()',
1402 ref: 'Array/first'
1403 },
1404 {
1405 name: 'initial',
1406 description: 'Returns all but the last n entries of the array.',
1407 sugar_compatibility: 2,
1408 sugar_notes: '_.initial does not exist in Sugar. Use a negative value for Array#to for the same effect.',
1409 original_code: '_.initial([1,2,3], 2)',
1410 sugar_code: '[1,2,3].to(-2)',
1411 ref: 'Array/to'
1412 },
1413 {
1414 name: 'last',
1415 description: 'Returns the last n entries of the array.',
1416 sugar_compatibility: 2,
1417 sugar_notes: '_.last exists in Sugar as Array#last, and is identical.',
1418 original_code: '_.last([1,2,3])',
1419 sugar_code: '[1,2,3].last()',
1420 ref: 'Array/last'
1421 },
1422 {
1423 name: 'rest',
1424 description: 'Returns the rest of the entries from a given index.',
1425 sugar_compatibility: 2,
1426 sugar_notes: '_.rest exists in Sugar as Array#from.',
1427 original_code: '_.rest([1,2,3], 2)',
1428 sugar_code: '[1,2,3].from(2)',
1429 ref: 'Array/from'
1430 },
1431 {
1432 name: 'compact',
1433 description: 'Returns a copy of the array with all falsy values removed.',
1434 sugar_compatibility: 3,
1435 sugar_notes: '_.compact exists in Sugar as Array#compact. Note that only undefined, null, and NaN are removed by default. To remove all falsy values, pass true as the first argument.',
1436 original_code: '_.compact([1,0,3,null])',
1437 sugar_code: '[1,0,3,null].compact(true)',
1438 ref: 'Array/compact'
1439 },
1440 {
1441 name: 'flatten',
1442 description: 'Flattens a nested array.',
1443 sugar_compatibility: 3,
1444 sugar_notes: '_.flatten exists in Sugar as Array#flatten. Sugar can additionally flatten to any level of depth, specified in the first argument (all levels by default).',
1445 original_code: '_.flatten([1,[2,3]])',
1446 sugar_code: '[1,[2,3]].flatten()',
1447 ref: 'Array/flatten'
1448 },
1449 {
1450 name: 'without',
1451 description: 'Returns a copy of the array with all instances of the values passed removed.',
1452 sugar_compatibility: 3,
1453 sugar_notes: '_.without exists in Sugar as Array#exclude, and is identical.',
1454 original_code: '_.without([1,2,3], 1)',
1455 sugar_code: '[1,2,3].exclude(1)',
1456 ref: 'Array/exclude'
1457 },
1458 {
1459 name: 'union',
1460 description: 'Computes the union of the arrays, or the unique items present in all arrays.',
1461 sugar_compatibility: 2,
1462 sugar_notes: '_.union exists in Sugar as Array#union, and is identical.',
1463 original_code: '_.union([1,2,3], [3,4,5])',
1464 sugar_code: '[1,2,3].union([3,4,5])',
1465 ref: 'Array/union'
1466 },
1467 {
1468 name: 'intersection',
1469 description: 'Computes the intersection of the arrays, or the values that are common to all arrays.',
1470 sugar_compatibility: 2,
1471 sugar_notes: '_.intersection exists in Sugar as Array#intersect, and is identical.',
1472 original_code: '_.intersect([1,2,3], [3,4,5])',
1473 sugar_code: '[1,2,3].intersect([3,4,5])',
1474 ref: 'Array/intersect'
1475 },
1476 {
1477 name: 'difference',
1478 description: 'Returns the values in the array that are not present in the others.',
1479 sugar_compatibility: 2,
1480 sugar_notes: '_.difference exists in Sugar as Array#subtract, which can subtract an indefinite number of arrays.',
1481 original_code: '_.difference([1,2,3], [3,4,5])',
1482 sugar_code: '[1,2,3].subtract([3,4,5])',
1483 ref: 'Array/subtract'
1484 },
1485 {
1486 name: 'uniq',
1487 description: 'Returns a duplicate-free version of the array.',
1488 sugar_compatibility: 3,
1489 sugar_notes: '_.uniq exists in Sugar as Array#unique. In addition to accepting a function to transform (map) the property on which to unique, Sugar will also accept a string that is a shortcut to this function. This would most commonly be the unique key of a JSON object, etc.',
1490 original_code: '_.uniq([1,2,1,3,1,4])',
1491 sugar_code: '[1,2,1,3,1,4].unique()',
1492 ref: 'Array/unique'
1493 },
1494 {
1495 name: 'zip',
1496 description: 'Merges together multiple arrays, creating a multi-dimensional array as the result.',
1497 sugar_compatibility: 2,
1498 sugar_notes: '_.zip exists in Sugar as Array#zip and is identical.',
1499 original_code: '_.zip(arr1, arr2)',
1500 sugar_code: 'arr1.zip(arr2)',
1501 ref: 'Array/zip'
1502 },
1503 {
1504 name: 'indexOf',
1505 description: 'Returns the index of the first value that matches in the array or -1 if not present.',
1506 sugar_compatibility: 2,
1507 sugar_notes: '_.indexOf exists natively in modern browsing engines as Array#indexOf. Sugar provides this method when it is not supported. Sugar also provides Array#findIndex for more complex index finding operations.',
1508 original_code: '_.indexOf([1,2,3], 1)',
1509 sugar_code: '[1,2,3].indexOf(1)',
1510 ref: 'Array/indexOf'
1511 },
1512 {
1513 name: 'lastIndexOf',
1514 description: 'Returns the index of the last value that matches in the array or -1 if not present.',
1515 sugar_compatibility: 2,
1516 sugar_notes: '_.lastIndexOf exists natively in modern browsing engines as Array#lastIndexOf. Sugar provides this method when it is not supported. Sugar also provides Array#findIndex for more complex index finding operations.',
1517 original_code: '_.lastIndexOf([1,2,3], 1)',
1518 sugar_code: '[1,2,3].lastIndexOf(1)',
1519 ref: 'Array/lastIndexOf'
1520 },
1521 {
1522 name: 'range',
1523 description: 'Shortcut to quickly create lists of integers.',
1524 sugar_compatibility: 3,
1525 sugar_notes: 'Ranges exist in Sugar and are created with Number.range. They can then be iterated over with the method "every".',
1526 original_code: '_.range(0, 30, 5)',
1527 sugar_code: 'Number.range(0, 30)',
1528 ref: 'Number/upto'
1529 },
1530 {
1531 name: 'bind',
1532 description: 'Binds an object to a function, making that object its "this" argument when called.',
1533 sugar_compatibility: 2,
1534 sugar_notes: '_.bind exists natively in modern browsing engines as Function#bind. Sugar provides this method when it is not supported.',
1535 original_code: '_.bind(fn, obj, 1)',
1536 sugar_code: 'fn.bind(obj, 1)',
1537 ref: 'Function/bind'
1538 },
1539 {
1540 name: 'bindAll',
1541 description: 'Binds a number of methods on the object.',
1542 sugar_compatibility: 0,
1543 sugar_notes: '_.bindAll does not exist in Sugar. However, the same functionality can be achieved through a workaround.',
1544 original_code: '_.bindAll(obj)',
1545 sugar_code: 'Object.each(obj, function(key, val){ if(Object.isFunction(val)) obj[key] = val.bind(obj); })',
1546 ref: 'Function/bind'
1547 },
1548 {
1549 name: 'memoize',
1550 description: 'Memoizes a given function by caching the computed result. Useful for speeding up slow-running computations.',
1551 sugar_compatibility: 1,
1552 sugar_notes: '_.memoize exists in Sugar as Function#once. It does not have the optional [hashFunction] parameter.',
1553 original_code: '_.memoize(fn)',
1554 sugar_code: 'fn.once()',
1555 ref: 'Function/once'
1556 },
1557 {
1558 name: 'delay',
1559 description: 'Invokes the function after n milliseconds.',
1560 sugar_compatibility: 2,
1561 sugar_notes: '_.delay exists in Sugar as Function#delay, and is identical.',
1562 original_code: '_.delay(fn, 1000, 1)',
1563 sugar_code: 'fn.delay(1000, 1)',
1564 ref: 'Function/delay'
1565 },
1566 {
1567 name: 'defer',
1568 description: 'Invokes the function after the current stack has cleared.',
1569 sugar_compatibility: 2,
1570 sugar_notes: '_.defer exists in Sugar as Function#delay, called with no arguments.',
1571 original_code: '_.defer(fn)',
1572 sugar_code: 'fn.delay()',
1573 ref: 'Function/delay'
1574 },
1575 {
1576 name: 'throttle',
1577 description: 'Creates a throttled version of the function that when invoked will only call the function at most once per n milliseconds. Useful for rate-limiting events.',
1578 sugar_compatibility: 3,
1579 sugar_notes: '_.throttle exists in Sugar as Function#throttle. In addition, Function#lazy has other options like accepting an upper limit to the queue of functions waiting to execute or execute the first time immediately.',
1580 original_code: '_.throttle(fn, 100)',
1581 sugar_code: 'fn.throttle(100)',
1582 ref: 'Function/lazy'
1583 },
1584 {
1585 name: 'debounce',
1586 description: 'Returns a "debounced" version of the function that will only execute once after n milliseconds have passed.',
1587 sugar_compatibility: 3,
1588 sugar_notes: '_.debounce exists in Sugar as Function#debounce.',
1589 original_code: '_.debounce(fn, 100)',
1590 sugar_code: 'fn.debounce(100)',
1591 ref: 'Function/debounce'
1592 },
1593 {
1594 name: 'once',
1595 description: 'Returns a version of the function that will return the value of the original call if called repeated times.',
1596 sugar_compatibility: 2,
1597 sugar_notes: '_.once exists in Sugar as Function#once. In Sugar it is identical to the functionality of _.memoize.',
1598 original_code: '_.once(fn)',
1599 sugar_code: 'fn.once()',
1600 ref: 'Function/once'
1601 },
1602 {
1603 name: 'after',
1604 description: 'Returns a version of the function that will only be run after being called n times.',
1605 sugar_compatibility: 2,
1606 sugar_notes: '_.after exists in Sugar as Function#after. The final callback will be passed an array of all argument objects collected (converted to proper arrays).',
1607 original_code: '_.after(5, fn)',
1608 sugar_code: 'fn.after(5)',
1609 ref: 'Function/after'
1610 },
1611 {
1612 name: 'wrap',
1613 description: 'Wraps the first function inside of the wrapper function, passing it as the first argument.',
1614 sugar_compatibility: 0,
1615 sugar_notes: '_.wrap does not exist in Sugar. However, Function#bind can be used to achieve the same effect in a pinch.',
1616 original_code: "_.wrap(fn, function(fn){ return fn() + 3; })",
1617 sugar_code: "(function(fn){ return fn() + 3; }).bind(null, fn)",
1618 ref: 'Function/bind'
1619 },
1620 {
1621 name: 'compose',
1622 description: 'Returns the composition of a list of functions.',
1623 sugar_compatibility: 0,
1624 sugar_notes: '_.compose does not exist in Sugar. However, Function#bind can help to achieve the same effect in a pinch.',
1625 original_code: "_.compose(fn1, fn2, *)",
1626 sugar_code: "Array.prototype.compose = function(){ var i = 0, fn; while(i < arguments.length){ fn = (function(){ return this.apply(this, arguments); }).bind(arguments[i]); i++; } return fn; } fn1.compose(fn2)",
1627 ref: 'Function/bind'
1628 },
1629 {
1630 name: 'keys',
1631 description: "Retrieves all the names of an object's properties.",
1632 sugar_compatibility: 3,
1633 sugar_notes: '_.keys exists natively in modern browsing engines as Object.keys. Sugar provides this method when it is not supported, and additionally allows a callback to be run against each key. Sugar can also create extended objects which have this method available as an instance method.',
1634 original_code: '_.keys(obj)',
1635 sugar_code: 'Object.keys(obj)',
1636 ref: 'Object/keys'
1637 },
1638 {
1639 name: 'values',
1640 description: "Retrieves all the values of an object's properties.",
1641 sugar_compatibility: 3,
1642 sugar_notes: '_.values exists in Sugar as Object.values, which can also accept a callback. Sugar can also create extended objects which have this method available as an instance method.',
1643 original_code: '_.values(obj)',
1644 sugar_code: 'Object.values(obj)',
1645 ref: 'Object/values'
1646 },
1647 {
1648 name: 'functions',
1649 description: 'Returns a sorted list of every method in the object.',
1650 sugar_compatibility: 0,
1651 sugar_notes: '_.functions does not exist in Sugar. However, Sugar makes it easy to reproduce the result.',
1652 original_code: '_.functions(obj)',
1653 sugar_code: 'Object.keys(obj).filter(function(key){ return Object.isFunction(obj[key]); })'
1654 },
1655 {
1656 name: 'extend',
1657 description: 'Copies all of the properties of the source object to the destination.',
1658 sugar_compatibility: 3,
1659 sugar_notes: "_.extend exists in Sugar as Object.merge. In the place of the ability to merge an unlimited number of objects, Sugar instead includes a parameter to determine how property conflicts should be resolved. However, extended objects can chain for the same effect.",
1660 original_code: '_.extend(obj1, obj2, obj3)',
1661 sugar_code: 'Object.extended(obj1).merge(obj2).merge(obj3)',
1662 ref: 'Object/merge'
1663 },
1664 {
1665 name: 'defaults',
1666 description: 'Fills in missing properties in the object with default values.',
1667 sugar_compatibility: 3,
1668 sugar_notes: "_.defaults can be achieved in Sugar by passing false as the last argument to Object.merge. This will indicate that conflicts should preserve the target object's properties. The third parameter is to indicate a shallow merge.",
1669 original_code: '_.defaults(obj, defaultProperties)',
1670 sugar_code: 'Object.merge(obj, defaultProperties, false, false)',
1671 ref: 'Object/merge'
1672 },
1673 {
1674 name: 'clone',
1675 description: 'Creates a shallow clone of the object.',
1676 sugar_compatibility: 3,
1677 sugar_notes: '_.clone exists in Sugar as Object.clone. Cloning is shallow by default but there is an option for deep cloning as well.',
1678 original_code: '_.clone(obj)',
1679 sugar_code: 'Object.clone(obj)',
1680 ref: 'Object/clone'
1681 },
1682 {
1683 name: 'tap',
1684 description: 'Invokes interceptor with the object, and then returns object. The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.',
1685 sugar_compatibility: 2,
1686 sugar_notes: '_.tap exists in Sugar as Object.tap. This method is mostly only useful when using extended objects or modifying the Object.prototype with Object.extend().',
1687 original_code: '_.tap(obj)',
1688 sugar_code: 'Object.tap(obj)',
1689 ref: 'Object/tap'
1690 },
1691 {
1692 name: 'isEqual',
1693 description: 'Performs a deep comparison between two objects to determine if they are equal.',
1694 sugar_compatibility: 2,
1695 sugar_notes: '_.isEqual exists in Sugar as Object.equal. Note also that in its instance method form the naming changes to "equals" for better readability.',
1696 original_code: '_.isEqual(obj1, obj2)',
1697 sugar_code: 'Object.equal(obj1, obj2)',
1698 ref: 'Object/equal'
1699 },
1700 {
1701 name: 'isElement',
1702 description: 'Returns true if the object is a DOM element.',
1703 sugar_compatibility: 0,
1704 sugar_notes: "_.isElement does not exist in Sugar, as it has no direct association with the DOM. However this functionality can be easily replicated (taken from Underscore's own implementation).",
1705 original_code: '_.isElement(obj1)',
1706 sugar_code: 'Object.isElement = function(obj){ return !!(obj && obj.nodeType == 1); }'
1707 },
1708 {
1709 name: 'isArray',
1710 description: 'Returns true if the object is an array.',
1711 sugar_compatibility: 2,
1712 sugar_notes: "_.isArray exists natively in modern browsing engines as Array.isArray. Sugar provides this when it is not supported and also implements it as Object.isArray to maintain a parallel with other type checking methods.",
1713 original_code: '_.isArray(obj)',
1714 sugar_code: 'Array.isArray(obj)',
1715 ref: 'Array/isArray'
1716 },
1717 {
1718 name: 'isArguments',
1719 description: 'Returns true if the object is an Arguments object.',
1720 sugar_compatibility: 2,
1721 sugar_notes: '_.isArguments does not exist in Sugar. A simple check of the "callee" parameter may be enough to simulate this (and is also cross-browser). Note that Sugar does have Array.create(), which will convert an arguments object into a standard array.',
1722 original_code: 'if(_.isArguments(obj))',
1723 sugar_code: 'if(obj.callee)'
1724 },
1725 {
1726 name: 'isFunction',
1727 description: 'Returns true if the object is a function.',
1728 sugar_compatibility: 2,
1729 sugar_notes: '_.isFunction exists as Object.isFunction and is identical.',
1730 original_code: '_.isFunction(obj)',
1731 sugar_code: 'Object.isFunction(obj)',
1732 ref: 'Object/isFunction'
1733 },
1734 {
1735 name: 'isString',
1736 description: 'Returns true if the object is a string.',
1737 sugar_compatibility: 2,
1738 sugar_notes: '_.isString exists as Object.isString and is identical.',
1739 original_code: '_.isString(obj)',
1740 sugar_code: 'Object.isString(obj)',
1741 ref: 'Object/isString'
1742 },
1743 {
1744 name: 'isNumber',
1745 description: 'Returns true if the object is a number or NaN.',
1746 sugar_compatibility: 2,
1747 sugar_notes: '_.isNumber exists as Object.isNumber and is identical.',
1748 original_code: '_.isNumber(obj)',
1749 sugar_code: 'Object.isNumber(obj)',
1750 ref: 'Object/isNumber'
1751 },
1752 {
1753 name: 'isBoolean',
1754 description: 'Returns true if the object is a boolean.',
1755 sugar_compatibility: 2,
1756 sugar_notes: '_.isBoolean exists as Object.isBoolean and is identical.',
1757 original_code: '_.isBoolean(obj)',
1758 sugar_code: 'Object.isBoolean(obj)',
1759 ref: 'Object/isBoolean'
1760 },
1761 {
1762 name: 'isDate',
1763 description: 'Returns true if the object is a date.',
1764 sugar_compatibility: 2,
1765 sugar_notes: '_.isDate exists as Object.isDate and is identical.',
1766 original_code: '_.isDate(obj)',
1767 sugar_code: 'Object.isDate(obj)',
1768 ref: 'Object/isDate'
1769 },
1770 {
1771 name: 'isRegExp',
1772 description: 'Returns true if the object is a RegExp.',
1773 sugar_compatibility: 2,
1774 sugar_notes: '_.isRegExp exists as Object.isRegExp and is identical.',
1775 original_code: '_.isRegExp(obj)',
1776 sugar_code: 'Object.isRegExp(obj)',
1777 ref: 'Object/isRegExp'
1778 },
1779 {
1780 name: 'isNaN',
1781 description: 'Returns true if the object is NaN.',
1782 sugar_compatibility: 2,
1783 sugar_notes: '_.isNaN exists as Object.isNaN and is identical.',
1784 original_code: '_.isNaN(obj)',
1785 sugar_code: 'Object.isNaN(obj)',
1786 ref: 'Object/isNaN'
1787 },
1788 {
1789 name: 'isNull',
1790 description: 'Returns true if the object is null.',
1791 sugar_compatibility: 0,
1792 sugar_notes: '_.isNull does not exist in Sugar. Just use a straight equality comparison.',
1793 original_code: '_.isNull(obj)',
1794 sugar_code: 'obj === null'
1795 },
1796 {
1797 name: 'isUndefined',
1798 description: 'Returns true if the object is undefined.',
1799 sugar_compatibility: 0,
1800 sugar_notes: '_.isUndefined does not exist in Sugar. Just use a straight equality comparison.',
1801 original_code: '_.isUndefined(obj)',
1802 sugar_code: 'obj === undefined'
1803 },
1804 {
1805 name: 'times',
1806 description: 'Invokes the passed iterator n times.',
1807 sugar_compatibility: 2,
1808 sugar_notes: '_.times exists in Sugar as Number#times and is identical.',
1809 original_code: '_.times(3, fn)',
1810 sugar_code: '(3).times(fn)',
1811 ref: 'Number/times'
1812 },
1813 {
1814 name: 'template',
1815 description: 'Compiles Javascript templates into functions that can be evaluated for rendering.',
1816 sugar_compatibility: 1,
1817 sugar_notes: '_.template exists in Sugar as String#assign with slightly different syntax. Although it does not have the complex "eval" functionality of Underscore, it can be useful for quickly assigning a value inside a string. String#assign will also accept numbers for arguments passed.',
1818 original_code: "_.template('hello: <%= name %>')({ name: 'joe' })",
1819 sugar_code: "'hello: {name}'.assign({ name: 'joe' })",
1820 ref: 'String/assign'
1821 }
1822 ]
1823}
1824];
1825
1826
1827 // END LIBS
1828
1829
1830 var URL_MATCH = /((?:https?|file):[^:]+(?::\d{4})?[^:]+):(\d+)(?::(\d+))?/;
1831 var warned = {};
1832
1833 var warn = function(message, stackLevel, skipMeta, docs, logLevel) {
1834 var stack, files, match, file, line;
1835 if(SUGAR_ANALYZER_UNIQUE_MESSAGES && hasBeenWarned(message)) {
1836 return;
1837 }
1838 stack = new Error().stack;
1839 message = message.replace(/\t/g, TS);
1840 if(stack) {
1841 files = stack.match(new RegExp(URL_MATCH.source, 'g'));
1842 var isConsole = stack.match(/console|Object\._evaluateOn/);
1843 file = files[stackLevel];
1844 if(!isConsole && (!file || file.match(new RegExp('(' + baseExcludePackages.concat(SUGAR_ANALYZER_EXCLUDES).join('|') + ')[^\/]*\.js')))) {
1845 return;
1846 }
1847 warned[message] = true;
1848 if(!skipMeta) {
1849 message += '\n\n';
1850 if(isConsole) {
1851 message += '----------- File: Console ---------';
1852 } else {
1853 match = file.match(URL_MATCH);
1854 message += '----------- File: ' + match[1] + ' ---------';
1855 if(match[2]) message += '\n----------- Line: ' + match[2] + ' --------------';
1856 if(match[3]) message += '\n----------- Char: ' + match[3] + ' --------------';
1857 }
1858 if(docs){
1859 message += '\n----------- Docs: http://sugarjs.com/api/' + docs + ' ---------';
1860 }
1861 }
1862 }
1863 if(SUGAR_ANALYZER_FIRST_LINE_ONLY) {
1864 message = message.replace(/\n[\S\s]+$/gm, '');
1865 }
1866 console[logLevel || globalLogLevel](message);
1867 };
1868
1869
1870 var hasBeenWarned = function(message) {
1871 return message in warned;
1872 }
1873
1874 var wrapAll = function(all) {
1875 for (var i = 0; i < all.length; i += 1) {
1876 wrapModule(all[i]);
1877 }
1878 }
1879
1880 var wrapModule = function(module){
1881 var namespace = this;
1882 if(module.namespace) {
1883 if(!namespace[module.namespace]) {
1884 namespace[module.namespace] = function(){};
1885 }
1886 namespace = namespace[module.namespace];
1887 }
1888 if(namespace && module.type == 'instance') namespace = namespace.prototype;
1889 for (var i = 0; i < module.methods.length; i++) {
1890 wrapMethod(namespace, module.methods[i])
1891 }
1892 }
1893
1894 var wrapMethod = function(namespace, method) {
1895 var fn = namespace[method.name] || function(){};
1896 namespace[method.name] = function() {
1897 var level, text = method.live_notes || method.sugar_notes;
1898 if(!method.hasOwnProperty('conflict')) method.conflict = true;
1899 var result = method.conflict && method.conflict.apply ? method.conflict.apply(this, arguments) : method.conflict;
1900 var cond = result && result.length ? result[0] : result;
1901 if(!cond && typeof method.conflict != 'function' && SUGAR_ANALYZER_INFO) {
1902 level = 'info';
1903 cond = true;
1904 }
1905 if(cond) {
1906 text = supplant(text, result);
1907 if(method.original_code && SUGAR_ANALYZER_SHOW_EXAMPLES){
1908 text += '\n\n';
1909 text += '\n'+library+': ' + method.original_code;
1910 text += '\nSugar: ' + method.sugar_code;
1911 text += '\n';
1912 }
1913 warn(text, 2, false, method.ref, level);
1914 }
1915 if(fn === PrototypeHash) {
1916 return new fn(arguments);
1917 } else {
1918 return fn.apply(this, arguments);
1919 }
1920 }
1921 };
1922
1923 function supplant(str, obj) {
1924 var val;
1925 return str.replace(/\{(.+?)\}/g, function(m, d) {
1926 val = obj[d];
1927 return val !== undefined ? jsonify(val) : m;
1928 });
1929 }
1930
1931 function jsonify(o){
1932 if(typeof JSON != 'undefined') {
1933 return JSON.stringify(o);
1934 } else {
1935 return o.toString();
1936 }
1937 }
1938
1939 function setDefault(name, defaultValue) {
1940 if(context[name] === undefined) {
1941 context[name] = defaultValue;
1942 }
1943 }
1944
1945
1946 var initialize = function(force) {
1947 var noneFound = true;
1948 if(typeof _ != 'undefined' || force) {
1949 noneFound = false;
1950 library = 'Underscore';
1951 globalLogLevel = 'info';
1952 wrapAll(SugarUnderscoreMethods);
1953 }
1954 if(typeof $A != 'undefined' || force) {
1955 noneFound = false;
1956 library = 'Prototype';
1957 globalLogLevel = 'warn';
1958 wrapAll(SugarPrototypeMethods);
1959 }
1960
1961 if(noneFound) {
1962 // No libs found, try initializing again after page load...
1963 window.addEventListener('load', function() {
1964 initialize(true);
1965 });
1966 return;
1967 }
1968
1969 var welcome =
1970 '### Welcome to the Sugar analyzer script! ###\n\n' +
1971 "As your program calls various methods, it will warn you about incompatibilities with Sugar, and give\n" +
1972 'suggestions about how to refactor. You can run this before refactoring to get a general idea about what needs to change\n' +
1973 'or you can immediately remove Prototype/Underscore for Sugar, let breakages happen, and fix as you go!' +
1974 '\n\nAnalyzer options (set these as globals):\n\n' +
1975 'SUGAR_ANALYZER_UNIQUE_MESSAGES = true/false | Display each message only once (default is true)\n' +
1976 'SUGAR_ANALYZER_FIRST_LINE_ONLY = true/false | Only display the first line of the message (default is false)\n' +
1977 'SUGAR_ANALYZER_SHOW_EXAMPLES = true/false | Show usage examples inline (default is true)\n' +
1978 "SUGAR_ANALYZER_EXCLUDES = ['a', 'b', ...] | Array of filenames to exclude messages from (default is [], can be partial match, leave off .js at the end)\n" +
1979 'SUGAR_ANALYZER_INFO = true/false | Display messages even when methods do not conflict (default is true)';
1980 //welcome += '\n\n#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#';
1981 console.info(welcome + '\n\n\n');
1982 //console.info('-------------------------------------------------------------------------------------------------------------------');
1983 }
1984
1985 var TS = ' ';
1986 var globalLogLevel;
1987 var library;
1988 var baseExcludePackages = ['prototype','underscore','analyzer'];
1989 var PrototypeHash = typeof Hash != 'undefined' ? Hash : null;
1990
1991 setDefault('SUGAR_ANALYZER_FIRST_LINE_ONLY', false);
1992 setDefault('SUGAR_ANALYZER_SHOW_EXAMPLES', true);
1993 setDefault('SUGAR_ANALYZER_INFO', true);
1994 setDefault('SUGAR_ANALYZER_UNIQUE_MESSAGES', true);
1995 setDefault('SUGAR_ANALYZER_EXCLUDES', []);
1996
1997 initialize();
1998
1999})(this);