handlebars-helpers 0.0.5 → 0.0.65

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +3 -3
  3. data/.gitignore +1 -0
  4. data/.handlebars_helpers.json +901 -0
  5. data/.handlebars_string_formatters.json +137 -0
  6. data/.rubocop.yml +4 -2
  7. data/.vscode/settings.json +6 -0
  8. data/Gemfile +3 -0
  9. data/Rakefile +1 -0
  10. data/STORIES.md +44 -6
  11. data/handlebars-helpers.gemspec +2 -0
  12. data/lib/handlebars/helpers.rb +2 -0
  13. data/lib/handlebars/helpers/base_helper.rb +36 -0
  14. data/lib/handlebars/helpers/base_safe_string_helper.rb +17 -0
  15. data/lib/handlebars/helpers/code_ruby/classify.rb +40 -0
  16. data/lib/handlebars/helpers/code_ruby/deconstantize.rb +48 -0
  17. data/lib/handlebars/helpers/code_ruby/demodulize.rb +58 -0
  18. data/lib/handlebars/helpers/code_ruby/foreign_key.rb +49 -0
  19. data/lib/handlebars/helpers/code_ruby/tableize.rb +35 -0
  20. data/lib/handlebars/helpers/comparison/and.rb +44 -0
  21. data/lib/handlebars/helpers/comparison/default.rb +66 -0
  22. data/lib/handlebars/helpers/comparison/eq.rb +35 -0
  23. data/lib/handlebars/helpers/comparison/gt.rb +35 -0
  24. data/lib/handlebars/helpers/comparison/gte.rb +43 -0
  25. data/lib/handlebars/helpers/comparison/lt.rb +35 -0
  26. data/lib/handlebars/helpers/comparison/lte.rb +43 -0
  27. data/lib/handlebars/helpers/comparison/ne.rb +35 -0
  28. data/lib/handlebars/helpers/comparison/or.rb +56 -0
  29. data/lib/handlebars/helpers/configuration.rb +72 -0
  30. data/lib/handlebars/helpers/inflection/ordinal.rb +58 -0
  31. data/lib/handlebars/helpers/inflection/ordinalize.rb +59 -0
  32. data/lib/handlebars/helpers/inflection/pluralize.rb +36 -0
  33. data/lib/handlebars/helpers/inflection/pluralize_by_number.rb +60 -0
  34. data/lib/handlebars/helpers/inflection/singularize.rb +35 -0
  35. data/lib/handlebars/helpers/register_helpers.rb +73 -0
  36. data/lib/handlebars/helpers/string_formatting/append_if.rb +42 -0
  37. data/lib/handlebars/helpers/string_formatting/back_slash.rb +33 -0
  38. data/lib/handlebars/helpers/string_formatting/camel.rb +29 -0
  39. data/lib/handlebars/helpers/string_formatting/constantize.rb +29 -0
  40. data/lib/handlebars/helpers/string_formatting/dasherize.rb +33 -0
  41. data/lib/handlebars/helpers/string_formatting/dotirize.rb +33 -0
  42. data/lib/handlebars/helpers/string_formatting/double_colon.rb +33 -0
  43. data/lib/handlebars/helpers/string_formatting/downcase.rb +31 -0
  44. data/lib/handlebars/helpers/string_formatting/format_as.rb +57 -0
  45. data/lib/handlebars/helpers/string_formatting/humanize.rb +39 -0
  46. data/lib/handlebars/helpers/string_formatting/lamel.rb +29 -0
  47. data/lib/handlebars/helpers/string_formatting/padl.rb +58 -0
  48. data/lib/handlebars/helpers/string_formatting/padr.rb +59 -0
  49. data/lib/handlebars/helpers/string_formatting/pluserize.rb +29 -0
  50. data/lib/handlebars/helpers/string_formatting/prepend_if.rb +42 -0
  51. data/lib/handlebars/helpers/string_formatting/singularize.rb +35 -0
  52. data/lib/handlebars/helpers/string_formatting/slash.rb +33 -0
  53. data/lib/handlebars/helpers/string_formatting/snake.rb +33 -0
  54. data/lib/handlebars/helpers/string_formatting/string.js +511 -0
  55. data/lib/handlebars/helpers/string_formatting/surround.rb +49 -0
  56. data/lib/handlebars/helpers/string_formatting/surround_if.rb +43 -0
  57. data/lib/handlebars/helpers/string_formatting/titleize.rb +39 -0
  58. data/lib/handlebars/helpers/string_formatting/upcase.rb +31 -0
  59. data/lib/handlebars/helpers/string_tokenizer.rb +43 -0
  60. data/lib/handlebars/helpers/template.rb +68 -0
  61. data/lib/handlebars/helpers/version.rb +1 -1
  62. metadata +82 -3
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # reference: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
4
+ require 'active_support/core_ext/string'
5
+
6
+ require 'handlebars/helpers/base_safe_string_helper'
7
+ require 'handlebars/helpers/string_formatting/format_as'
8
+
9
+ module Handlebars
10
+ module Helpers
11
+ # String manipulation methods for case formatting
12
+ module StringFormatting
13
+ # Append If will prepend the prefix to value, if value is not empty
14
+ class PrependIf < Handlebars::Helpers::BaseSafeStringHelper
15
+ # Parse will Append If will prepend the prefix to value, if value is not empty
16
+ #
17
+ # @example
18
+ #
19
+ # puts PrependIf.new.parse('turn to symbol', ':', 'snake')
20
+ #
21
+ # :turn_to_symbol
22
+ #
23
+ # @param [String] value - value to add prepend too
24
+ # @param [String] prefix - prefix to insert in front of value
25
+ # @param [String] formats - list of formats to apply to value, defaults to none
26
+ # @return [String] prefix + value when value exists, otherwise ''
27
+ def parse(value, prefix, formats)
28
+ format_as = Handlebars::Helpers::StringFormatting::FormatAs.new
29
+ value.present? ? "#{prefix}#{format_as.parse(value, formats)}" : ''
30
+ end
31
+
32
+ def handlebars_helper
33
+ proc do |_context, value, prefix, formats|
34
+ # Handle optional: formats
35
+ formats = nil if formats.is_a?(V8::Object)
36
+ wrapper(parse(value, prefix, formats))
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # reference: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
4
+ require 'active_support/core_ext/string'
5
+
6
+ require 'handlebars/helpers/base_helper'
7
+
8
+ module Handlebars
9
+ module Helpers
10
+ # General purpose string manipulation helpers
11
+ module StringFormatting
12
+ # The reverse of #pluralize, returns the singular form of a word in a string
13
+ class Singularize < Handlebars::Helpers::BaseHelper
14
+ # Parse will reverse of #pluralize, returns the singular form of a word in a string
15
+ #
16
+ # @example
17
+ #
18
+ # puts Singularize.new.parse('names')
19
+ #
20
+ # name
21
+ #
22
+ # puts Singularize.new.parse('octopi')
23
+ #
24
+ # octopus
25
+ #
26
+ # @return [String] plural value turned to singular value
27
+ def parse(value)
28
+ return '' if value.nil?
29
+
30
+ value.singularize
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # reference: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
4
+ require 'active_support/core_ext/string'
5
+
6
+ require 'handlebars/helpers/base_helper'
7
+
8
+ module Handlebars
9
+ module Helpers
10
+ # String manipulation methods for case formatting
11
+ module StringFormatting
12
+ # slash case the characters in the given 'string'.
13
+ class Slash < Handlebars::Helpers::BaseHelper
14
+ # Parse will slash case the characters in the given 'string'.
15
+ #
16
+ # @side effects
17
+ #
18
+ # Text casing is preserved.
19
+ #
20
+ # @example
21
+ #
22
+ # puts Slash.new.parse('the Quick brown Fox 99')
23
+ #
24
+ # the/Quick/brown/Fox99
25
+ #
26
+ # @return [String] value converted to slash notation
27
+ def parse(value)
28
+ tokenizer.parse(value, preserve_case: true, separator: '/')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # reference: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
4
+ require 'active_support/core_ext/string'
5
+
6
+ require 'handlebars/helpers/base_helper'
7
+
8
+ module Handlebars
9
+ module Helpers
10
+ # String manipulation methods for case formatting
11
+ module StringFormatting
12
+ # snake case the characters in the given 'string'.
13
+ class Snake < Handlebars::Helpers::BaseHelper
14
+ # Parse will snake case the characters in the given 'string'.
15
+ #
16
+ # @side effects
17
+ #
18
+ # All text is in lower case
19
+ #
20
+ # @example
21
+ #
22
+ # puts Snake.new.parse('the quick brown fox 99')
23
+ #
24
+ # the-quick-brown-fox99
25
+ #
26
+ # @return [String] value converted to snake case
27
+ def parse(value)
28
+ tokenizer.parse(value, separator: '_')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,511 @@
1
+ /**
2
+ * Center a string using non-breaking spaces
3
+ *
4
+ * @param {String} `str`
5
+ * @param {String} `spaces`
6
+ * @return {String}
7
+ * @api public
8
+ */
9
+
10
+ helpers.center = function(str, spaces) {
11
+ if (!util.isString(str)) return '';
12
+ var space = '';
13
+ var i = 0;
14
+ while (i < spaces) {
15
+ space += '&nbsp;';
16
+ i++;
17
+ }
18
+ return space + str + space;
19
+ };
20
+
21
+ /**
22
+ * Like trim, but removes both extraneous whitespace **and
23
+ * non-word characters** from the beginning and end of a string.
24
+ *
25
+ * ```handlebars
26
+ * {{chop "_ABC_"}}
27
+ * <!-- results in: 'ABC' -->
28
+ *
29
+ * {{chop "-ABC-"}}
30
+ * <!-- results in: 'ABC' -->
31
+ *
32
+ * {{chop " ABC "}}
33
+ * <!-- results in: 'ABC' -->
34
+ * ```
35
+ * @param {String} `string` The string to chop.
36
+ * @return {String}
37
+ * @api public
38
+ */
39
+
40
+ helpers.chop = function(str) {
41
+ if (!util.isString(str)) return '';
42
+ return utils.chop(str);
43
+ };
44
+
45
+
46
+ /**
47
+ * Truncates a string to the specified `length`, and appends
48
+ * it with an elipsis, `…`.
49
+ *
50
+ * ```handlebars
51
+ * {{ellipsis (sanitize "<span>foo bar baz</span>"), 7}}
52
+ * <!-- results in: 'foo bar…' -->
53
+ * {{ellipsis "foo bar baz", 7}}
54
+ * <!-- results in: 'foo bar…' -->
55
+ * ```
56
+ * @param {String} `str`
57
+ * @param {Number} `length` The desired length of the returned string.
58
+ * @return {String} The truncated string.
59
+ * @api public
60
+ */
61
+
62
+ helpers.ellipsis = function(str, limit) {
63
+ if (util.isString(str)) {
64
+ if (str.length <= limit) {
65
+ return str;
66
+ }
67
+ return helpers.truncate(str, limit) + '…';
68
+ }
69
+ };
70
+
71
+ /**
72
+ * Return true if `value` is a string.
73
+ *
74
+ * ```handlebars
75
+ * {{isString "foo"}}
76
+ * <!-- results in: 'true' -->
77
+ * ```
78
+ * @param {String} `value`
79
+ * @return {Boolean}
80
+ * @api public
81
+ */
82
+
83
+ helpers.isString = function(value) {
84
+ return typeof value === 'string';
85
+ };
86
+
87
+ /**
88
+ * Return the number of occurrences of `substring` within the
89
+ * given `string`.
90
+ *
91
+ * ```handlebars
92
+ * {{occurrences "foo bar foo bar baz" "foo"}}
93
+ * <!-- results in: 2 -->
94
+ * ```
95
+ * @param {String} `str`
96
+ * @param {String} `substring`
97
+ * @return {Number} Number of occurrences
98
+ * @api public
99
+ */
100
+
101
+ helpers.occurrences = function(str, substring) {
102
+ if (!util.isString(str)) return '';
103
+ var len = substring.length;
104
+ var pos = 0;
105
+ var n = 0;
106
+
107
+ while ((pos = str.indexOf(substring, pos)) > -1) {
108
+ n++;
109
+ pos += len;
110
+ }
111
+ return n;
112
+ };
113
+
114
+ /**
115
+ * Replace spaces in the given string with pluses.
116
+ *
117
+ * ```handlebars
118
+ * {{plusify "foo bar baz"}}
119
+ * <!-- results in: 'foo+bar+baz' -->
120
+ * ```
121
+ * @param {String} `str` The input string
122
+ * @return {String} Input string with spaces replaced by plus signs
123
+ * @source Stephen Way <https://github.com/stephenway>
124
+ * @api public
125
+ */
126
+
127
+ helpers.plusify = function(str, ch) {
128
+ if (!util.isString(str)) return '';
129
+ if (!util.isString(ch)) ch = ' ';
130
+ return str.split(ch).join('+');
131
+ };
132
+
133
+ /**
134
+ * Prepends the given `string` with the specified `prefix`.
135
+ *
136
+ * ```handlebars
137
+ * <!-- given that "val" is "bar" -->
138
+ * {{prepend val "foo-"}}
139
+ * <!-- results in: 'foo-bar' -->
140
+ * ```
141
+ * @param {String} `str`
142
+ * @param {String} `prefix`
143
+ * @return {String}
144
+ * @api public
145
+ */
146
+
147
+ helpers.prepend = function(str, prefix) {
148
+ return typeof str === 'string' && typeof prefix === 'string'
149
+ ? (prefix + str)
150
+ : str;
151
+ };
152
+
153
+ /**
154
+ * Render a block without processing mustache templates inside the block.
155
+ *
156
+ * ```handlebars
157
+ * {{{{#raw}}}}
158
+ * {{foo}}
159
+ * {{{{/raw}}}}
160
+ * <!-- results in: '{{foo}}' -->
161
+ * ```
162
+ *
163
+ * @param {Object} `options`
164
+ * @return {String}
165
+ * @block
166
+ * @api public
167
+ */
168
+
169
+ helpers.raw = function(options) {
170
+ var str = options.fn();
171
+ var opts = util.options(this, options);
172
+ if (opts.escape !== false) {
173
+ var idx = 0;
174
+ while (((idx = str.indexOf('{{', idx)) !== -1)) {
175
+ if (str[idx - 1] !== '\\') {
176
+ str = str.slice(0, idx) + '\\' + str.slice(idx);
177
+ }
178
+ idx += 3;
179
+ }
180
+ }
181
+ return str;
182
+ };
183
+
184
+ /**
185
+ * Remove all occurrences of `substring` from the given `str`.
186
+ *
187
+ * ```handlebars
188
+ * {{remove "a b a b a b" "a "}}
189
+ * <!-- results in: 'b b b' -->
190
+ * ```
191
+ * @param {String} `str`
192
+ * @param {String} `substring`
193
+ * @return {String}
194
+ * @api public
195
+ */
196
+
197
+ helpers.remove = function(str, ch) {
198
+ if (!util.isString(str)) return '';
199
+ if (!util.isString(ch)) return str;
200
+ return str.split(ch).join('');
201
+ };
202
+
203
+ /**
204
+ * Remove the first occurrence of `substring` from the given `str`.
205
+ *
206
+ * ```handlebars
207
+ * {{remove "a b a b a b" "a"}}
208
+ * <!-- results in: ' b a b a b' -->
209
+ * ```
210
+ * @param {String} `str`
211
+ * @param {String} `substring`
212
+ * @return {String}
213
+ * @api public
214
+ */
215
+
216
+ helpers.removeFirst = function(str, ch) {
217
+ if (!util.isString(str)) return '';
218
+ if (!util.isString(ch)) return str;
219
+ return str.replace(ch, '');
220
+ };
221
+
222
+ /**
223
+ * Replace all occurrences of substring `a` with substring `b`.
224
+ *
225
+ * ```handlebars
226
+ * {{replace "a b a b a b" "a" "z"}}
227
+ * <!-- results in: 'z b z b z b' -->
228
+ * ```
229
+ * @param {String} `str`
230
+ * @param {String} `a`
231
+ * @param {String} `b`
232
+ * @return {String}
233
+ * @api public
234
+ */
235
+
236
+ helpers.replace = function(str, a, b) {
237
+ if (!util.isString(str)) return '';
238
+ if (!util.isString(a)) return str;
239
+ if (!util.isString(b)) b = '';
240
+ return str.split(a).join(b);
241
+ };
242
+
243
+ /**
244
+ * Replace the first occurrence of substring `a` with substring `b`.
245
+ *
246
+ * ```handlebars
247
+ * {{replace "a b a b a b" "a" "z"}}
248
+ * <!-- results in: 'z b a b a b' -->
249
+ * ```
250
+ * @param {String} `str`
251
+ * @param {String} `a`
252
+ * @param {String} `b`
253
+ * @return {String}
254
+ * @api public
255
+ */
256
+
257
+ helpers.replaceFirst = function(str, a, b) {
258
+ if (!util.isString(str)) return '';
259
+ if (!util.isString(a)) return str;
260
+ if (!util.isString(b)) b = '';
261
+ return str.replace(a, b);
262
+ };
263
+
264
+ /**
265
+ * Reverse a string.
266
+ *
267
+ * ```handlebars
268
+ * {{reverse "abcde"}}
269
+ * <!-- results in: 'edcba' -->
270
+ * ```
271
+ * @param {String} `str`
272
+ * @return {String}
273
+ * @api public
274
+ */
275
+
276
+ helpers.reverse = function(str) {
277
+ if (!util.isString(str)) return '';
278
+ return str.split('').reverse().join('');
279
+ };
280
+
281
+ /**
282
+ * Sentence case the given string
283
+ *
284
+ * ```handlebars
285
+ * {{sentence "hello world. goodbye world."}}
286
+ * <!-- results in: 'Hello world. Goodbye world.' -->
287
+ * ```
288
+ * @param {String} `str`
289
+ * @return {String}
290
+ * @api public
291
+ */
292
+
293
+ helpers.sentence = function(str) {
294
+ if (!util.isString(str)) return '';
295
+ return str.replace(/((?:\S[^\.\?\!]*)[\.\?\!]*)/g, function(txt) {
296
+ return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
297
+ });
298
+ };
299
+
300
+ /**
301
+ * snake_case the characters in the given `string`.
302
+ *
303
+ * ```handlebars
304
+ * {{snakecase "a-b-c d_e"}}
305
+ * <!-- results in: 'a_b_c_d_e' -->
306
+ * ```
307
+ * @param {String} `string`
308
+ * @return {String}
309
+ * @api public
310
+ */
311
+
312
+ helpers.snakecase = function(str) {
313
+ if (!util.isString(str)) return '';
314
+ return utils.changecase(str, function(ch) {
315
+ return '_' + ch;
316
+ });
317
+ };
318
+
319
+ /**
320
+ * Split `string` by the given `character`.
321
+ *
322
+ * ```handlebars
323
+ * {{split "a,b,c" ","}}
324
+ * <!-- results in: ['a', 'b', 'c'] -->
325
+ * ```
326
+ * @param {String} `string` The string to split.
327
+ * @return {String} `character` Default is an empty string.
328
+ * @api public
329
+ */
330
+
331
+ helpers.split = function(str, ch) {
332
+ if (!util.isString(str)) return '';
333
+ if (!util.isString(ch)) ch = ',';
334
+ return str.split(ch);
335
+ };
336
+
337
+ /**
338
+ * Tests whether a string begins with the given prefix.
339
+ *
340
+ * ```handlebars
341
+ * {{#startsWith "Goodbye" "Hello, world!"}}
342
+ * Whoops
343
+ * {{else}}
344
+ * Bro, do you even hello world?
345
+ * {{/startsWith}}
346
+ * ```
347
+ * @contributor Dan Fox <http://github.com/iamdanfox>
348
+ * @param {String} `prefix`
349
+ * @param {String} `testString`
350
+ * @param {String} `options`
351
+ * @return {String}
352
+ * @block
353
+ * @api public
354
+ */
355
+
356
+ helpers.startsWith = function(prefix, str, options) {
357
+ var args = [].slice.call(arguments);
358
+ options = args.pop();
359
+ if (util.isString(str) && str.indexOf(prefix) === 0) {
360
+ return options.fn(this);
361
+ }
362
+ if (typeof options.inverse === 'function') {
363
+ return options.inverse(this);
364
+ }
365
+ return '';
366
+ };
367
+
368
+ /**
369
+ * Title case the given string.
370
+ *
371
+ * ```handlebars
372
+ * {{titleize "this is title case"}}
373
+ * <!-- results in: 'This Is Title Case' -->
374
+ * ```
375
+ * @param {String} `str`
376
+ * @return {String}
377
+ * @api public
378
+ */
379
+
380
+ helpers.titleize = function(str) {
381
+ if (!util.isString(str)) return '';
382
+ var title = str.replace(/[- _]+/g, ' ');
383
+ var words = title.split(' ');
384
+ var len = words.length;
385
+ var res = [];
386
+ var i = 0;
387
+ while (len--) {
388
+ var word = words[i++];
389
+ res.push(exports.capitalize(word));
390
+ }
391
+ return res.join(' ');
392
+ };
393
+
394
+ /**
395
+ * Removes extraneous whitespace from the beginning and end
396
+ * of a string.
397
+ *
398
+ * ```handlebars
399
+ * {{trim " ABC "}}
400
+ * <!-- results in: 'ABC' -->
401
+ * ```
402
+ * @param {String} `string` The string to trim.
403
+ * @return {String}
404
+ * @api public
405
+ */
406
+
407
+ helpers.trim = function(str) {
408
+ return typeof str === 'string' ? str.trim() : '';
409
+ };
410
+
411
+ /**
412
+ * Removes extraneous whitespace from the beginning of a string.
413
+ *
414
+ * ```handlebars
415
+ * {{trim " ABC "}}
416
+ * <!-- results in: 'ABC ' -->
417
+ * ```
418
+ * @param {String} `string` The string to trim.
419
+ * @return {String}
420
+ * @api public
421
+ */
422
+
423
+ helpers.trimLeft = function(str) {
424
+ if (util.isString(str)) {
425
+ return str.replace(/^\s+/, '');
426
+ }
427
+ };
428
+
429
+ /**
430
+ * Removes extraneous whitespace from the end of a string.
431
+ *
432
+ * ```handlebars
433
+ * {{trimRight " ABC "}}
434
+ * <!-- results in: ' ABC' -->
435
+ * ```
436
+ * @param {String} `string` The string to trim.
437
+ * @return {String}
438
+ * @api public
439
+ */
440
+
441
+ helpers.trimRight = function(str) {
442
+ if (util.isString(str)) {
443
+ return str.replace(/\s+$/, '');
444
+ }
445
+ };
446
+
447
+ /**
448
+ * Truncate a string to the specified `length`. Also see [ellipsis](#ellipsis).
449
+ *
450
+ * ```handlebars
451
+ * truncate("foo bar baz", 7);
452
+ * <!-- results in: 'foo bar' -->
453
+ * truncate(sanitize("<span>foo bar baz</span>", 7));
454
+ * <!-- results in: 'foo bar' -->
455
+ * ```
456
+ * @param {String} `str`
457
+ * @param {Number} `limit` The desired length of the returned string.
458
+ * @param {String} `suffix` Optionally supply a string to use as a suffix to
459
+ * denote when the string has been truncated. Otherwise an ellipsis (`…`) will be used.
460
+ * @return {String} The truncated string.
461
+ * @api public
462
+ */
463
+
464
+ helpers.truncate = function(str, limit, suffix) {
465
+ if (util.isString(str)) {
466
+ if (typeof suffix !== 'string') {
467
+ suffix = '';
468
+ }
469
+ if (str.length > limit) {
470
+ return str.slice(0, limit - suffix.length) + suffix;
471
+ }
472
+ return str;
473
+ }
474
+ };
475
+
476
+ /**
477
+ * Truncate a string to have the specified number of words.
478
+ * Also see [truncate](#truncate).
479
+ *
480
+ * ```handlebars
481
+ * truncateWords("foo bar baz", 1);
482
+ * <!-- results in: 'foo…' -->
483
+ * truncateWords("foo bar baz", 2);
484
+ * <!-- results in: 'foo bar…' -->
485
+ * truncateWords("foo bar baz", 3);
486
+ * <!-- results in: 'foo bar baz' -->
487
+ * ```
488
+ * @param {String} `str`
489
+ * @param {Number} `limit` The desired length of the returned string.
490
+ * @param {String} `suffix` Optionally supply a string to use as a suffix to
491
+ * denote when the string has been truncated.
492
+ * @return {String} The truncated string.
493
+ * @api public
494
+ */
495
+
496
+ helpers.truncateWords = function(str, count, suffix) {
497
+ if (util.isString(str) && isNumber(count)) {
498
+ if (typeof suffix !== 'string') {
499
+ suffix = '…';
500
+ }
501
+
502
+ var num = Number(count);
503
+ var arr = str.split(/[ \t]/);
504
+ if (num > arr.length) {
505
+ arr = arr.slice(0, num);
506
+ }
507
+
508
+ var val = arr.join(' ').trim();
509
+ return val + suffix;
510
+ }
511
+ };