ultimate-base 0.2.4 → 0.3.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/.gitignore +4 -1
  2. data/.rvmrc +0 -1
  3. data/.rvmrc.example +0 -1
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +113 -1
  6. data/app/assets/javascripts/ultimate/backbone/base.js.coffee +14 -2
  7. data/app/assets/javascripts/ultimate/backbone/extra/jquery-plugin-adapter.js.coffee +1 -1
  8. data/app/assets/javascripts/ultimate/backbone/model.js.coffee +6 -0
  9. data/app/assets/javascripts/ultimate/backbone/view.js.coffee +5 -4
  10. data/app/assets/javascripts/ultimate/backbone/views/slider.js.coffee +4 -1
  11. data/app/assets/javascripts/ultimate/helpers/asset_tag.js.coffee +71 -54
  12. data/app/assets/javascripts/ultimate/helpers/base.js.coffee +3 -0
  13. data/app/assets/javascripts/ultimate/helpers/decor.js.coffee +19 -14
  14. data/app/assets/javascripts/ultimate/helpers/number.js.coffee +600 -0
  15. data/app/assets/javascripts/ultimate/helpers/record_tag.js.coffee +66 -0
  16. data/app/assets/javascripts/ultimate/helpers/tag.js.coffee +77 -0
  17. data/app/assets/javascripts/ultimate/helpers/url.js.coffee +49 -0
  18. data/app/assets/javascripts/ultimate/helpers.js.coffee +39 -66
  19. data/app/assets/javascripts/ultimate/{devise.js.coffee → improves/devise.js.coffee} +0 -0
  20. data/app/assets/javascripts/ultimate/improves/i18n-lite.js.coffee +18 -12
  21. data/app/assets/javascripts/ultimate/improves/magic-radios.js.coffee +2 -0
  22. data/app/assets/javascripts/ultimate/improves/typed-fields.js.coffee +13 -14
  23. data/app/assets/javascripts/ultimate/{base.js.coffee → jquery.base.js.coffee} +24 -37
  24. data/app/assets/javascripts/ultimate/{experimental/_inflections → underscore}/underscore.inflection.js +19 -18
  25. data/app/assets/javascripts/ultimate/underscore/underscore.outcasts.js.coffee +123 -0
  26. data/app/assets/javascripts/ultimate/underscore/underscore.string.js +16 -6
  27. data/config/routes.rb +2 -0
  28. data/lib/{ultimate-base → ultimate/base}/engine.rb +1 -0
  29. data/lib/{ultimate-base → ultimate/base}/version.rb +1 -1
  30. data/lib/ultimate/base.rb +10 -0
  31. data/lib/{ultimate-base → ultimate}/extensions/directive_processor.rb +0 -0
  32. data/lib/{ultimate-base → ultimate}/extensions/sass_script_functions.rb +0 -0
  33. data/scripts/rails +8 -0
  34. data/test/dummy/Rakefile +7 -0
  35. data/test/dummy/_emfile +18 -0
  36. data/{app/assets/javascripts/ultimate/helpers/forms.js.coffee → test/dummy/app/assets/images/.gitkeep} +0 -0
  37. data/test/dummy/app/assets/javascripts/application.js +3 -0
  38. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  39. data/test/dummy/app/assets/stylesheets/global/forms.css.scss +65 -0
  40. data/test/dummy/app/assets/stylesheets/global/layout/footer.scss +18 -0
  41. data/test/dummy/app/assets/stylesheets/global/layout/header.scss +13 -0
  42. data/test/dummy/app/assets/stylesheets/global/layout/main-menu.scss +68 -0
  43. data/test/dummy/app/assets/stylesheets/global/layout.css.scss +46 -0
  44. data/test/dummy/app/assets/stylesheets/global/reserved.css.scss +79 -0
  45. data/test/dummy/app/assets/stylesheets/global/reset.css.scss +85 -0
  46. data/test/dummy/app/assets/stylesheets/global/structures.css.scss +9 -0
  47. data/test/dummy/app/assets/stylesheets/import/base.scss +34 -0
  48. data/test/dummy/app/assets/stylesheets/plugins/qunit.css.scss +3 -0
  49. data/test/dummy/app/controllers/application_controller.rb +4 -0
  50. data/test/dummy/app/controllers/main_controller.rb +8 -0
  51. data/test/dummy/app/helpers/application_helper.rb +2 -0
  52. data/test/dummy/app/mailers/.gitkeep +0 -0
  53. data/test/dummy/app/models/.gitkeep +0 -0
  54. data/test/dummy/app/views/application/_footer.html.haml +3 -0
  55. data/test/dummy/app/views/application/_header.html.haml +4 -0
  56. data/test/dummy/app/views/application/_main_menu.html.haml +11 -0
  57. data/test/dummy/app/views/layouts/application.html.haml +24 -0
  58. data/test/dummy/app/views/main/index.html.haml +13 -0
  59. data/test/dummy/app/views/main/qunit.html.haml +7 -0
  60. data/test/dummy/config/application.rb +59 -0
  61. data/test/dummy/config/boot.rb +10 -0
  62. data/test/dummy/config/database.yml +25 -0
  63. data/test/dummy/config/environment.rb +5 -0
  64. data/test/dummy/config/environments/development.rb +40 -0
  65. data/test/dummy/config/environments/production.rb +67 -0
  66. data/test/dummy/config/environments/test.rb +37 -0
  67. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  68. data/test/dummy/config/initializers/inflections.rb +15 -0
  69. data/test/dummy/config/initializers/mime_types.rb +5 -0
  70. data/test/dummy/config/initializers/qunit-rails.rb +1 -0
  71. data/test/dummy/config/initializers/secret_token.rb +7 -0
  72. data/test/dummy/config/initializers/session_store.rb +8 -0
  73. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  74. data/test/dummy/config/locales/en.yml +5 -0
  75. data/test/dummy/config/routes.rb +63 -0
  76. data/test/dummy/config.ru +4 -0
  77. data/test/dummy/log/.gitkeep +0 -0
  78. data/test/dummy/public/404.html +26 -0
  79. data/test/dummy/public/422.html +26 -0
  80. data/test/dummy/public/500.html +25 -0
  81. data/test/dummy/public/favicon.ico +0 -0
  82. data/test/dummy/script/rails +6 -0
  83. data/test/javascripts/all_tests.js.coffee +13 -0
  84. data/test/javascripts/test_helper.js.coffee +3 -0
  85. data/test/javascripts/tests/base_test.js.coffee +42 -0
  86. data/test/javascripts/tests/helpers/asset_tag_test.js.coffee +51 -0
  87. data/test/javascripts/tests/helpers/number_test.js.coffee +250 -0
  88. data/test/javascripts/tests/helpers/record_tag_test.js.coffee +56 -0
  89. data/test/javascripts/tests/helpers/tag_test.js.coffee +33 -0
  90. data/test/javascripts/tests/helpers/url_test.js.coffee +28 -0
  91. data/test/javascripts/tests/helpers_test.js.coffee +47 -0
  92. data/test/javascripts/tests/improves/i18n-lite_test.js.coffee +25 -0
  93. data/test/javascripts/tests/underscore/underscore.outcasts.test.js.coffee +38 -0
  94. data/test/stylesheets/test_helper.css +4 -0
  95. data/ultimate-base.gemspec +4 -4
  96. metadata +116 -24
  97. data/app/assets/javascripts/ultimate/bus.js.coffee +0 -57
  98. data/app/assets/javascripts/ultimate/experimental/_inflections/dzone.inflections.js +0 -154
  99. data/app/assets/javascripts/ultimate/helpers/array.js.coffee +0 -63
  100. data/app/assets/javascripts/ultimate/helpers/tags.js.coffee +0 -73
  101. data/app/assets/javascripts/ultimate/widgets/dock.js.coffee +0 -70
  102. data/app/assets/javascripts/ultimate/widgets/gear.js.coffee +0 -84
  103. data/app/assets/javascripts/ultimate/widgets/jquery-ext.js.coffee +0 -104
  104. data/app/assets/javascripts/ultimate/widgets/jquery.adapter.js.coffee +0 -62
  105. data/app/assets/javascripts/ultimate/widgets/widget.js.coffee +0 -115
  106. data/lib/ultimate-base.rb +0 -10
@@ -0,0 +1,600 @@
1
+ #= require ./base
2
+
3
+ @Ultimate.Helpers.Number =
4
+
5
+ DEFAULTS:
6
+ # Used in number_to_delimited
7
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
8
+ format:
9
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
10
+ separator: "."
11
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
12
+ delimiter: ","
13
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
14
+ precision: 3
15
+ # If set to true, precision will mean the number of significant digits instead
16
+ # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
17
+ significant: false
18
+ # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
19
+ strip_insignificant_zeros: false
20
+
21
+ # Used in number_to_currency
22
+ currency:
23
+ format:
24
+ format: "%u%n"
25
+ negative_format: "-%u%n"
26
+ unit: "$"
27
+ # These five are to override number.format and are optional
28
+ separator: "."
29
+ delimiter: ","
30
+ precision: 2
31
+ significant: false
32
+ strip_insignificant_zeros: false
33
+
34
+ # Used in number_to_percentage
35
+ percentage:
36
+ format:
37
+ delimiter: ""
38
+ format: "%n%"
39
+
40
+ # Used in number_to_rounded
41
+ precision:
42
+ format:
43
+ delimiter: ""
44
+
45
+ # Used in number_to_human_size and number_to_human
46
+ human:
47
+ format:
48
+ # These five are to override number.format and are optional
49
+ delimiter: ""
50
+ precision: 3
51
+ significant: true
52
+ strip_insignificant_zeros: true
53
+ # Used in number_to_human_size
54
+ storage_units:
55
+ # Storage units output formatting.
56
+ # %u is the storage unit, %n is the number (default: 2 MB)
57
+ format: "%n %u"
58
+ units:
59
+ byte: "Bytes"
60
+ kb: "KB"
61
+ mb: "MB"
62
+ gb: "GB"
63
+ tb: "TB"
64
+ # Used in number_to_human
65
+ decimal_units:
66
+ format: "%n %u"
67
+ # Decimal units output formatting
68
+ # By default we will only quantify some of the exponents
69
+ # but the commented ones might be defined or overridden
70
+ # by the user.
71
+ units:
72
+ # femto: Quadrillionth
73
+ # pico: Trillionth
74
+ # nano: Billionth
75
+ # micro: Millionth
76
+ # mili: Thousandth
77
+ # centi: Hundredth
78
+ # deci: Tenth
79
+ unit: ""
80
+ # ten:
81
+ # one: Ten
82
+ # other: Tens
83
+ # hundred: Hundred
84
+ thousand: "Thousand"
85
+ million: "Million"
86
+ billion: "Billion"
87
+ trillion: "Trillion"
88
+ quadrillion: "Quadrillion"
89
+
90
+ DECIMAL_UNITS:
91
+ '0' : 'unit'
92
+ '1' : 'ten'
93
+ '2' : 'hundred'
94
+ '3' : 'thousand'
95
+ '6' : 'million'
96
+ '9' : 'billion'
97
+ '12' : 'trillion'
98
+ '15' : 'quadrillion'
99
+ '-1' : 'deci'
100
+ '-2' : 'centi'
101
+ '-3' : 'mili'
102
+ '-6' : 'micro'
103
+ '-9' : 'nano'
104
+ '-12': 'pico'
105
+ '-15': 'femto'
106
+
107
+ STORAGE_UNITS: ['byte', 'kb', 'mb', 'gb', 'tb']
108
+
109
+
110
+
111
+ ###**
112
+ * Formats a +number+ into a US phone number (e.g., (555)
113
+ * 123-9876). You can customize the format in the +options+ hash.
114
+ *
115
+ * ==== Options
116
+ *
117
+ * * <tt>'area_code'</tt> - Adds parentheses around the area code.
118
+ * * <tt>'delimiter'</tt> - Specifies the delimiter to use
119
+ * (defaults to "-").
120
+ * * <tt>'extension'</tt> - Specifies an extension to add to the
121
+ * end of the generated number.
122
+ * * <tt>'country_code'</tt> - Sets the country code for the phone
123
+ * number.
124
+ * ==== Examples
125
+ *
126
+ * number_to_phone(5551234) # => 555-1234
127
+ * number_to_phone("5551234") # => 555-1234
128
+ * number_to_phone(1235551234) # => 123-555-1234
129
+ * number_to_phone(1235551234, area_code: true) # => (123) 555-1234
130
+ * number_to_phone(1235551234, delimiter: ' ') # => 123 555 1234
131
+ * number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
132
+ * number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
133
+ * number_to_phone("123a456") # => 123a456
134
+ *
135
+ * number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
136
+ * # => +1.123.555.1234 x 1343
137
+ ###
138
+ number_to_phone: (number, options = {}) ->
139
+ return "" unless number?
140
+ number = _.string.strip(String(number))
141
+ area_code = options['area_code']
142
+ delimiter = options['delimiter'] ? "-"
143
+ extension = options['extension']
144
+ country_code = options['country_code']
145
+ if area_code
146
+ number = number.replace(/(\d{1,3})(\d{3})(\d{4}$)/, "($1) $2#{delimiter}$3")
147
+ else
148
+ number = number.replace(/(\d{0,3})(\d{3})(\d{4})$/, "$1#{delimiter}$2#{delimiter}$3")
149
+ number = number.slice(1) if _.string.startsWith(number, delimiter) and not _.string.isBlank(delimiter)
150
+ buf = []
151
+ buf.push "+#{country_code}#{delimiter}" unless _.string.isBlank(country_code)
152
+ buf.push number
153
+ buf.push " x #{extension}" unless _.string.isBlank(extension)
154
+ buf.join('')
155
+
156
+ ###**
157
+ * Formats a +number+ into a currency string (e.g., $13.65). You
158
+ * can customize the format in the +options+ hash.
159
+ *
160
+ * ==== Options
161
+ *
162
+ * * <tt>'locale'</tt> - Sets the locale to be used for formatting
163
+ * (defaults to current locale).
164
+ * * <tt>'precision'</tt> - Sets the level of precision (defaults
165
+ * to 2).
166
+ * * <tt>'unit'</tt> - Sets the denomination of the currency
167
+ * (defaults to "$").
168
+ * * <tt>'separator'</tt> - Sets the separator between the units
169
+ * (defaults to ".").
170
+ * * <tt>'delimiter'</tt> - Sets the thousands delimiter (defaults
171
+ * to ",").
172
+ * * <tt>'format'</tt> - Sets the format for non-negative numbers
173
+ * (defaults to "%u%n"). Fields are <tt>%u</tt> for the
174
+ * currency, and <tt>%n</tt> for the number.
175
+ * * <tt>'negative_format'</tt> - Sets the format for negative
176
+ * numbers (defaults to prepending an hyphen to the formatted
177
+ * number given by <tt>'format'</tt>). Accepts the same fields
178
+ * than <tt>'format'</tt>, except <tt>%n</tt> is here the
179
+ * absolute value of the number.
180
+ *
181
+ * ==== Examples
182
+ *
183
+ * number_to_currency(1234567890.50) # => $1,234,567,890.50
184
+ * number_to_currency(1234567890.506) # => $1,234,567,890.51
185
+ * number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
186
+ * number_to_currency(1234567890.506, locale: 'fr') # => 1 234 567 890,51 €
187
+ * number_to_currency('123a456') # => $123a456
188
+ *
189
+ * number_to_currency(-1234567890.50, negative_format: '(%u%n)')
190
+ * # => ($1,234,567,890.50)
191
+ * number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '')
192
+ * # => &pound;1234567890,50
193
+ * number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
194
+ * # => 1234567890,50 &pound;
195
+ ###
196
+ number_to_currency: (number, options = {}) ->
197
+ return "" unless number?
198
+
199
+ # TODO replace `currency` with `defaults`
200
+ currency = @i18n_format_options(options['locale'], 'currency')
201
+ currency['negative_format'] ||= "-" + currency['format'] if currency['format']
202
+
203
+ defaults = _.extend(@default_format_options('currency'), currency)
204
+ defaults['negative_format'] = "-" + options['format'] if options['format']
205
+ options = _.extend(defaults, options)
206
+
207
+ unit = _.outcasts.delete(options, 'unit')
208
+ format = _.outcasts.delete(options, 'format')
209
+
210
+ if number < 0
211
+ format = _.outcasts.delete(options, 'negative_format')
212
+ number = Math.abs(number)
213
+
214
+ format.replace('%n', @number_to_rounded(number, options)).replace('%u', unit)
215
+
216
+
217
+ ###**
218
+ * Formats a +number+ as a percentage string (e.g., 65%). You can
219
+ * customize the format in the +options+ hash.
220
+ *
221
+ * ==== Options
222
+ *
223
+ * * <tt>'locale'</tt> - Sets the locale to be used for formatting
224
+ * (defaults to current locale).
225
+ * * <tt>'precision'</tt> - Sets the precision of the number
226
+ * (defaults to 3).
227
+ * * <tt>'significant'</tt> - If +true+, precision will be the #
228
+ * of significant_digits. If +false+, the # of fractional
229
+ * digits (defaults to +false+).
230
+ * * <tt>'separator'</tt> - Sets the separator between the
231
+ * fractional and integer digits (defaults to ".").
232
+ * * <tt>'delimiter'</tt> - Sets the thousands delimiter (defaults
233
+ * to "").
234
+ * * <tt>'strip_insignificant_zeros'</tt> - If +true+ removes
235
+ * insignificant zeros after the decimal separator (defaults to
236
+ * +false+).
237
+ * * <tt>'format'</tt> - Specifies the format of the percentage
238
+ * string The number field is <tt>%n</tt> (defaults to "%n%").
239
+ *
240
+ * ==== Examples
241
+ *
242
+ * number_to_percentage(100) # => 100.000%
243
+ * number_to_percentage('98') # => 98.000%
244
+ * number_to_percentage(100, precision: 0) # => 100%
245
+ * number_to_percentage(1000, delimiter: '.', separator: ,') # => 1.000,000%
246
+ * number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
247
+ * number_to_percentage(1000, 'locale' => 'fr') # => 1 000,000%
248
+ * number_to_percentage('98a') # => 98a%
249
+ * number_to_percentage(100, format: '%n %') # => 100 %
250
+ ###
251
+ number_to_percentage: (number, options = {}) ->
252
+ return "" unless number?
253
+ defaults = @format_options(options['locale'], 'percentage')
254
+ options = _.extend(defaults, options)
255
+ format = options['format'] or "%n%"
256
+ format.replace('%n', @number_to_rounded(number, options))
257
+
258
+ ###**
259
+ * Formats a +number+ with grouped thousands using +delimiter+
260
+ * (e.g., 12,324). You can customize the format in the +options+
261
+ * hash.
262
+ *
263
+ * ==== Options
264
+ *
265
+ * * <tt>'locale'</tt> - Sets the locale to be used for formatting
266
+ * (defaults to current locale).
267
+ * * <tt>'delimiter'</tt> - Sets the thousands delimiter (defaults
268
+ * to ",").
269
+ * * <tt>'separator'</tt> - Sets the separator between the
270
+ * fractional and integer digits (defaults to ".").
271
+ *
272
+ * ==== Examples
273
+ *
274
+ * number_to_delimited(12345678) # => 12,345,678
275
+ * number_to_delimited('123456') # => 123,456
276
+ * number_to_delimited(12345678.05) # => 12,345,678.05
277
+ * number_to_delimited(12345678, delimiter: '.') # => 12.345.678
278
+ * number_to_delimited(12345678, delimiter: ',') # => 12,345,678
279
+ * number_to_delimited(12345678.05, separator: ' ') # => 12,345,678 05
280
+ * number_to_delimited(12345678.05, locale: 'fr') # => 12 345 678,05
281
+ * number_to_delimited('112a') # => 112a
282
+ * number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
283
+ * # => 98 765 432,98
284
+ ###
285
+ number_to_delimited: (number, options = {}) ->
286
+ return "" unless number?
287
+ return number unless @valid_float(number)
288
+ options = _.extend(@format_options(options['locale']), options)
289
+ parts = String(number).split('.')
290
+ parts[0] = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1#{options['delimiter']}")
291
+ parts.join(options['separator'])
292
+
293
+
294
+ round_with_precision: (number, precision = 2) ->
295
+ precision = Math.pow(10, precision)
296
+ Math.round(number * precision) / precision
297
+
298
+ ###**
299
+ * Formats a +number+ with the specified level of
300
+ * <tt>'precision'</tt> (e.g., 112.32 has a precision of 2 if
301
+ * +'significant'+ is +false+, and 5 if +'significant'+ is +true+).
302
+ * You can customize the format in the +options+ hash.
303
+ *
304
+ * ==== Options
305
+ *
306
+ * * <tt>'locale'</tt> - Sets the locale to be used for formatting
307
+ * (defaults to current locale).
308
+ * * <tt>'precision'</tt> - Sets the precision of the number
309
+ * (defaults to 3).
310
+ * * <tt>'significant'</tt> - If +true+, precision will be the #
311
+ * of significant_digits. If +false+, the # of fractional
312
+ * digits (defaults to +false+).
313
+ * * <tt>'separator'</tt> - Sets the separator between the
314
+ * fractional and integer digits (defaults to ".").
315
+ * * <tt>'delimiter'</tt> - Sets the thousands delimiter (defaults
316
+ * to "").
317
+ * * <tt>'strip_insignificant_zeros'</tt> - If +true+ removes
318
+ * insignificant zeros after the decimal separator (defaults to
319
+ * +false+).
320
+ *
321
+ * ==== Examples
322
+ *
323
+ * number_to_rounded(111.2345) # => 111.235
324
+ * number_to_rounded(111.2345, precision: 2) # => 111.23
325
+ * number_to_rounded(13, precision: 5) # => 13.00000
326
+ * number_to_rounded(389.32314, precision: 0) # => 389
327
+ * number_to_rounded(111.2345, significant: true) # => 111
328
+ * number_to_rounded(111.2345, precision: 1, significant: true) # => 100
329
+ * number_to_rounded(13, precision: 5, significant: true) # => 13.000
330
+ * number_to_rounded(111.234, locale: 'fr') # => 111,234
331
+ *
332
+ * number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
333
+ * # => 13
334
+ *
335
+ * number_to_rounded(389.32314, precision: 4, significant: true) # => 389.3
336
+ * number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
337
+ * # => 1.111,23
338
+ ###
339
+ number_to_rounded: (number, options = {}) ->
340
+ return "" unless number?
341
+ return number unless @valid_float(number)
342
+ number = Number(number)
343
+ options = _.extend(@format_options(options['locale'], 'precision'), options)
344
+
345
+ precision = _.outcasts.delete(options, 'precision')
346
+ significant = _.outcasts.delete(options, 'significant')
347
+ strip_insignificant_zeros = _.outcasts.delete(options, 'strip_insignificant_zeros')
348
+
349
+ if significant and precision > 0
350
+ if number == 0
351
+ [digits, rounded_number] = [1, 0]
352
+ else
353
+ digits = Math.floor(Math.log(Math.abs(number)) / Math.LN10 + 1)
354
+ rounded_number = @round_with_precision(number, precision - digits)
355
+ digits = Math.floor(Math.log(Math.abs(rounded_number)) / Math.LN10 + 1) # After rounding, the number of digits may have changed
356
+ precision -= digits
357
+ precision = 0 if precision < 0 # don't let it be negative
358
+ else
359
+ rounded_number = @round_with_precision(number, precision)
360
+ formatted_number = @number_to_delimited(_.string.sprintf("%01.#{precision}f", rounded_number), options)
361
+ if strip_insignificant_zeros
362
+ escaped_separator = _.string.escapeRegExp(options['separator'])
363
+ formatted_number
364
+ .replace(new RegExp("(#{escaped_separator})(\\d*[1-9])?0+$"), '$1$2')
365
+ .replace(new RegExp("#{escaped_separator}$"), '')
366
+ else
367
+ formatted_number
368
+
369
+ ###**
370
+ * Formats the bytes in +number+ into a more understandable
371
+ * representation (e.g., giving it 1500 yields 1.5 KB). This
372
+ * method is useful for reporting file sizes to users. You can
373
+ * customize the format in the +options+ hash.
374
+ *
375
+ * See <tt>number_to_human</tt> if you want to pretty-print a
376
+ * generic number.
377
+ *
378
+ * ==== Options
379
+ *
380
+ * * <tt>'locale'</tt> - Sets the locale to be used for formatting
381
+ * (defaults to current locale).
382
+ * * <tt>'precision'</tt> - Sets the precision of the number
383
+ * (defaults to 3).
384
+ * * <tt>'significant'</tt> - If +true+, precision will be the #
385
+ * of significant_digits. If +false+, the # of fractional
386
+ * digits (defaults to +true+)
387
+ * * <tt>'separator'</tt> - Sets the separator between the
388
+ * fractional and integer digits (defaults to ".").
389
+ * * <tt>'delimiter'</tt> - Sets the thousands delimiter (defaults
390
+ * to "").
391
+ * * <tt>'strip_insignificant_zeros'</tt> - If +true+ removes
392
+ * insignificant zeros after the decimal separator (defaults to
393
+ * +true+)
394
+ * * <tt>'prefix'</tt> - If +'si'+ formats the number using the SI
395
+ * prefix (defaults to 'binary')
396
+ *
397
+ * ==== Examples
398
+ *
399
+ * number_to_human_size(123) # => 123 Bytes
400
+ * number_to_human_size(1234) # => 1.21 KB
401
+ * number_to_human_size(12345) # => 12.1 KB
402
+ * number_to_human_size(1234567) # => 1.18 MB
403
+ * number_to_human_size(1234567890) # => 1.15 GB
404
+ * number_to_human_size(1234567890123) # => 1.12 TB
405
+ * number_to_human_size(1234567, precision: 2) # => 1.2 MB
406
+ * number_to_human_size(483989, precision: 2) # => 470 KB
407
+ * number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
408
+ *
409
+ * Non-significant zeros after the fractional separator are stripped out by
410
+ * default (set <tt>'strip_insignificant_zeros'</tt> to +false+ to change that):
411
+ *
412
+ * number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
413
+ * number_to_human_size(524288000, precision: 5) # => "500 MB"
414
+ ###
415
+ number_to_human_size: (number, options = {}) ->
416
+ return "" unless number?
417
+ return number unless @valid_float(number)
418
+
419
+ number = Number(number)
420
+ defaults = @format_options(options['locale'], 'human')
421
+ options = _.extend(defaults, options)
422
+
423
+ #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
424
+ options['strip_insignificant_zeros'] = true if not _.has(options, 'strip_insignificant_zeros')
425
+ storage_units_format = @translate_number_value_with_default('human.storage_units.format', locale: options['locale'], raise: true)
426
+
427
+ base = if options['prefix'] is 'si' then 1000 else 1024
428
+ if parseInt(number) < base
429
+ unit = @translate_number_value_with_default('human.storage_units.units.byte', locale: options['locale'], count: parseInt(number), raise: true)
430
+ storage_units_format.replace(/%n/, parseInt(number)).replace(/%u/, unit)
431
+ else
432
+ max_exp = @STORAGE_UNITS.length - 1
433
+ exponent = parseInt(Math.log(number) / Math.log(base)) # Convert to base
434
+ exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
435
+ number /= Math.pow(base, exponent)
436
+ unit_key = @STORAGE_UNITS[exponent]
437
+ unit = @translate_number_value_with_default("human.storage_units.units.#{unit_key}", locale: options['locale'], count: number, raise: true)
438
+ formatted_number = @number_to_rounded(number, options)
439
+ storage_units_format.replace(/%n/, formatted_number).replace(/%u/, unit)
440
+
441
+ ###**
442
+ * Pretty prints (formats and approximates) a number in a way it
443
+ * is more readable by humans (eg.: 1200000000 becomes "1.2
444
+ * Billion"). This is useful for numbers that can get very large
445
+ * (and too hard to read).
446
+ *
447
+ * See <tt>number_to_human_size</tt> if you want to print a file
448
+ * size.
449
+ *
450
+ * You can also define you own unit-quantifier names if you want
451
+ * to use other decimal units (eg.: 1500 becomes "1.5
452
+ * kilometers", 0.150 becomes "150 milliliters", etc). You may
453
+ * define a wide range of unit quantifiers, even fractional ones
454
+ * (centi, deci, mili, etc).
455
+ *
456
+ * ==== Options
457
+ *
458
+ * * <tt>'locale'</tt> - Sets the locale to be used for formatting
459
+ * (defaults to current locale).
460
+ * * <tt>'precision'</tt> - Sets the precision of the number
461
+ * (defaults to 3).
462
+ * * <tt>'significant'</tt> - If +true+, precision will be the #
463
+ * of significant_digits. If +false+, the # of fractional
464
+ * digits (defaults to +true+)
465
+ * * <tt>'separator'</tt> - Sets the separator between the
466
+ * fractional and integer digits (defaults to ".").
467
+ * * <tt>'delimiter'</tt> - Sets the thousands delimiter (defaults
468
+ * to "").
469
+ * * <tt>'strip_insignificant_zeros'</tt> - If +true+ removes
470
+ * insignificant zeros after the decimal separator (defaults to
471
+ * +true+)
472
+ * * <tt>'units'</tt> - A Hash of unit quantifier names. Or a
473
+ * string containing an i18n scope where to find this hash. It
474
+ * might have the following keys:
475
+ * * *integers*: <tt>'unit'</tt>, <tt>'ten'</tt>,
476
+ * *<tt>'hundred'</tt>, <tt>'thousand'</tt>, <tt>'million'</tt>,
477
+ * *<tt>'billion'</tt>, <tt>'trillion'</tt>,
478
+ * *<tt>'quadrillion'</tt>
479
+ * * *fractionals*: <tt>'deci'</tt>, <tt>'centi'</tt>,
480
+ * *<tt>'mili'</tt>, <tt>'micro'</tt>, <tt>'nano'</tt>,
481
+ * *<tt>'pico'</tt>, <tt>'femto'</tt>
482
+ * * <tt>'format'</tt> - Sets the format of the output string
483
+ * (defaults to "%n %u"). The field types are:
484
+ * * %u - The quantifier (ex.: 'thousand')
485
+ * * %n - The number
486
+ *
487
+ * ==== Examples
488
+ *
489
+ * number_to_human(123) # => "123"
490
+ * number_to_human(1234) # => "1.23 Thousand"
491
+ * number_to_human(12345) # => "12.3 Thousand"
492
+ * number_to_human(1234567) # => "1.23 Million"
493
+ * number_to_human(1234567890) # => "1.23 Billion"
494
+ * number_to_human(1234567890123) # => "1.23 Trillion"
495
+ * number_to_human(1234567890123456) # => "1.23 Quadrillion"
496
+ * number_to_human(1234567890123456789) # => "1230 Quadrillion"
497
+ * number_to_human(489939, precision: 2) # => "490 Thousand"
498
+ * number_to_human(489939, precision: 4) # => "489.9 Thousand"
499
+ * number_to_human(1234567, precision: 4,
500
+ * significant: false) # => "1.2346 Million"
501
+ * number_to_human(1234567, precision: 1,
502
+ * separator: ',',
503
+ * significant: false) # => "1,2 Million"
504
+ *
505
+ * Non-significant zeros after the decimal separator are stripped
506
+ * out by default (set <tt>'strip_insignificant_zeros'</tt> to
507
+ * +false+ to change that):
508
+ *
509
+ * number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
510
+ * number_to_human(500000000, precision: 5) # => "500 Million"
511
+ *
512
+ * ==== Custom Unit Quantifiers
513
+ *
514
+ * You can also use your own custom unit quantifiers:
515
+ * number_to_human(500000, 'units' => {'unit' => "ml", 'thousand' => "lt"}) # => "500 lt"
516
+ *
517
+ * If in your I18n locale you have:
518
+ *
519
+ * distance:
520
+ * centi:
521
+ * one: "centimeter"
522
+ * other: "centimeters"
523
+ * unit:
524
+ * one: "meter"
525
+ * other: "meters"
526
+ * thousand:
527
+ * one: "kilometer"
528
+ * other: "kilometers"
529
+ * billion: "gazillion-distance"
530
+ *
531
+ * Then you could do:
532
+ *
533
+ * number_to_human(543934, 'units' => 'distance') # => "544 kilometers"
534
+ * number_to_human(54393498, 'units' => 'distance') # => "54400 kilometers"
535
+ * number_to_human(54393498000, 'units' => 'distance') # => "54.4 gazillion-distance"
536
+ * number_to_human(343, 'units' => 'distance', 'precision' => 1) # => "300 meters"
537
+ * number_to_human(1, 'units' => 'distance') # => "1 meter"
538
+ * number_to_human(0.34, 'units' => 'distance') # => "34 centimeters"
539
+ ###
540
+ number_to_human: (number, options = {}) ->
541
+ return "" unless number?
542
+ return number unless @valid_float(number)
543
+ number = Number(number)
544
+ defaults = @format_options(options['locale'], 'human')
545
+ options = _.extend(defaults, options)
546
+ #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
547
+ options['strip_insignificant_zeros'] = true unless _.has(options, 'strip_insignificant_zeros')
548
+ inverted_du = _.outcasts.invert(@DECIMAL_UNITS)
549
+ units = _.outcasts.delete options, 'units'
550
+ unit_exponents = if _.isObject(units)
551
+ units
552
+ else if _.isString(units)
553
+ I18n.translate(units, locale: options['locale'], raise: true)
554
+ else unless units?
555
+ @translate_number_value_with_default("human.decimal_units.units", locale: options['locale'], raise: true)
556
+ else
557
+ throw new Error "'units' must be a Hash or String translation scope."
558
+ unit_exponents = _.map(_.keys(unit_exponents), (e_name) -> parseInt(inverted_du[e_name])).sort((a, b) -> a < b)
559
+ number_exponent = if number isnt 0 then Math.floor(Math.log(Math.abs(number)) / Math.LN10) else 0
560
+ display_exponent = _.find(unit_exponents, (e) -> number_exponent >= e) or 0
561
+ number /= Math.pow(10, display_exponent)
562
+ unit = if _.isObject(units)
563
+ units[@DECIMAL_UNITS[display_exponent]]
564
+ else if _.isString(units)
565
+ I18n.translate("#{units}.#{@DECIMAL_UNITS[display_exponent]}", locale: options['locale'], count: parseInt(number))
566
+ else
567
+ @translate_number_value_with_default("human.decimal_units.units.#{@DECIMAL_UNITS[display_exponent]}", locale: options['locale'], count: parseInt(number))
568
+ decimal_format = options['format'] or @translate_number_value_with_default('human.decimal_units.format', locale: options['locale'])
569
+ formatted_number = @number_to_rounded(number, options)
570
+ _.string.strip decimal_format.replace(/%n/, formatted_number).replace(/%u/, unit)
571
+
572
+
573
+
574
+ #################### Private ####################
575
+
576
+ #:nodoc:
577
+ format_options: (locale, namespace = null) ->
578
+ _.extend(@default_format_options(namespace), @i18n_format_options(locale, namespace))
579
+
580
+ #:nodoc:
581
+ default_format_options: (namespace = null) ->
582
+ options = _.clone(@DEFAULTS['format'])
583
+ _.extend(options, @DEFAULTS[namespace]['format']) if namespace?
584
+ options
585
+
586
+ #:nodoc:
587
+ i18n_format_options: (locale, namespace = null) ->
588
+ options = _.clone(I18n.translate('number.format', locale: locale, default: {}))
589
+ if namespace?
590
+ _.extend(options, I18n.translate("number.#{namespace}.format", locale: locale, default: {}))
591
+ options
592
+
593
+ #:nodoc:
594
+ translate_number_value_with_default: (key, i18n_options = {}) ->
595
+ _default = _.reduce(key.split('.'), ((defaults, k) -> defaults[k]), @DEFAULTS)
596
+ I18n.translate(key, _.extend({default: _default, scope: 'number'}, i18n_options))
597
+
598
+ #:nodoc:
599
+ valid_float: (number) ->
600
+ not isNaN(number)
@@ -0,0 +1,66 @@
1
+ #= require ./base
2
+ #= require ./tag
3
+
4
+ @Ultimate.Helpers.RecordTag =
5
+
6
+ div_for: (record, args...) ->
7
+ @content_tag_for "div", record, args...
8
+
9
+ content_tag_for: (tag_name, single_or_multiple_records, prefix = null, options = null, block = null) ->
10
+ block = _.outcasts.blockGiven(arguments)
11
+ if _.isFunction(single_or_multiple_records.map)
12
+ single_or_multiple_records.map( (single_record) =>
13
+ @content_tag_for_single_record(tag_name, single_record, prefix, options, block)
14
+ ).join("\n")
15
+ else
16
+ @content_tag_for_single_record(tag_name, single_or_multiple_records, prefix, options, block)
17
+
18
+ content_tag_for_single_record: (tag_name, record, prefix, options, block = null) ->
19
+ [options, prefix] = [prefix, null] if _.isObject(prefix)
20
+ block = _.outcasts.blockGiven(arguments)
21
+ options = if _.isObject(options) then _.clone(options) else {}
22
+ _.extend options, {class: Ultimate.Helpers.Tag.concat_class(@dom_class(record, prefix), options['class']), id: @dom_id(record, prefix)}
23
+ if block
24
+ Ultimate.Helpers.Tag.content_tag(tag_name, block(record), options)
25
+ else
26
+ Ultimate.Helpers.Tag.content_tag(tag_name, block, options)
27
+
28
+ # ============= from ActionController::RecordIdentifier ===============
29
+
30
+ # The DOM class convention is to use the singular form of an object or class. Examples:
31
+ #
32
+ # dom_class(post) # => "post"
33
+ # dom_class(Person) # => "person"
34
+ #
35
+ # If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
36
+ #
37
+ # dom_class(post, :edit) # => "edit_post"
38
+ # dom_class(Person, :edit) # => "edit_person"
39
+ dom_class: (record_or_class, prefix = "") ->
40
+ # TODO improve as in Ultimate.Backbone.Model
41
+ if _.isString(record_or_class)
42
+ singular = _.singularize(record_or_class)
43
+ else unless singular = _.result(record_or_class, 'singular')
44
+ if _.isString(record_or_class?.constructor?.name)
45
+ singular = _.singularize(_.string.underscored(record_or_class.constructor.name))
46
+ if prefix then "#{prefix}_#{singular}" else singular
47
+
48
+ # The DOM id convention is to use the singular form of an object or class with the id following an underscore.
49
+ # If no id is found, prefix with "new_" instead. Examples:
50
+ #
51
+ # dom_id(Post.find(45)) # => "post_45"
52
+ # dom_id(new Post) # => "new_post"
53
+ #
54
+ # If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
55
+ #
56
+ # dom_id(Post.find(45), "edit") # => "edit_post_45"
57
+ # TODO sync with rorId and ror_id
58
+ dom_id: (record, prefix = "") ->
59
+ if record_id = @sanitize_dom_id(record.id)
60
+ "#{@dom_class(record, prefix)}_#{record_id}"
61
+ else
62
+ @dom_class(record, prefix or "new")
63
+
64
+ # Replaces characters that are invalid in HTML DOM ids with valid ones.
65
+ sanitize_dom_id: (candidate_id) ->
66
+ candidate_id # TODO implement conversion to valid DOM id values