master
Raw Download raw file
   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);