|
45 | 45 | return obj != null && typeof obj === 'object' && (propName in obj);
|
46 | 46 | }
|
47 | 47 |
|
| 48 | + /** |
| 49 | + * Safe way of detecting whether or not the given thing is a primitive and |
| 50 | + * whether it has the given property |
| 51 | + */ |
| 52 | + function primitiveHasOwnProperty (primitive, propName) { |
| 53 | + return ( |
| 54 | + primitive != null |
| 55 | + && typeof primitive !== 'object' |
| 56 | + && primitive.hasOwnProperty |
| 57 | + && primitive.hasOwnProperty(propName) |
| 58 | + ); |
| 59 | + } |
| 60 | + |
48 | 61 | // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
|
49 | 62 | // See https://github.com/janl/mustache.js/issues/189
|
50 | 63 | var regExpTest = RegExp.prototype.test;
|
|
377 | 390 | if (cache.hasOwnProperty(name)) {
|
378 | 391 | value = cache[name];
|
379 | 392 | } else {
|
380 |
| - var context = this, names, index, lookupHit = false; |
| 393 | + var context = this, intermediateValue, names, index, lookupHit = false; |
381 | 394 |
|
382 | 395 | while (context) {
|
383 | 396 | if (name.indexOf('.') > 0) {
|
384 |
| - value = context.view; |
| 397 | + intermediateValue = context.view; |
385 | 398 | names = name.split('.');
|
386 | 399 | index = 0;
|
387 | 400 |
|
|
395 | 408 | *
|
396 | 409 | * This is specially necessary for when the value has been set to
|
397 | 410 | * `undefined` and we want to avoid looking up parent contexts.
|
| 411 | + * |
| 412 | + * In the case where dot notation is used, we consider the lookup |
| 413 | + * to be successful even if the last "object" in the path is |
| 414 | + * not actually an object but a primitive (e.g., a string, or an |
| 415 | + * integer), because it is sometimes useful to access a property |
| 416 | + * of an autoboxed primitive, such as the length of a string. |
398 | 417 | **/
|
399 |
| - while (value != null && index < names.length) { |
| 418 | + while (intermediateValue != null && index < names.length) { |
400 | 419 | if (index === names.length - 1)
|
401 |
| - lookupHit = hasProperty(value, names[index]); |
| 420 | + lookupHit = ( |
| 421 | + hasProperty(intermediateValue, names[index]) |
| 422 | + || primitiveHasOwnProperty(intermediateValue, names[index]) |
| 423 | + ); |
402 | 424 |
|
403 |
| - value = value[names[index++]]; |
| 425 | + intermediateValue = intermediateValue[names[index++]]; |
404 | 426 | }
|
405 | 427 | } else {
|
406 |
| - value = context.view[name]; |
| 428 | + intermediateValue = context.view[name]; |
| 429 | + |
| 430 | + /** |
| 431 | + * Only checking against `hasProperty`, which always returns `false` if |
| 432 | + * `context.view` is not an object. Deliberately omitting the check |
| 433 | + * against `primitiveHasOwnProperty` if dot notation is not used. |
| 434 | + * |
| 435 | + * Consider this example: |
| 436 | + * ``` |
| 437 | + * Mustache.render("The length of a football field is {{#length}}{{length}}{{/length}}.", {length: "100 yards"}) |
| 438 | + * ``` |
| 439 | + * |
| 440 | + * If we were to check also against `primitiveHasOwnProperty`, as we do |
| 441 | + * in the dot notation case, then render call would return: |
| 442 | + * |
| 443 | + * "The length of a football field is 9." |
| 444 | + * |
| 445 | + * rather than the expected: |
| 446 | + * |
| 447 | + * "The length of a football field is 100 yards." |
| 448 | + **/ |
407 | 449 | lookupHit = hasProperty(context.view, name);
|
408 | 450 | }
|
409 | 451 |
|
410 |
| - if (lookupHit) |
| 452 | + if (lookupHit) { |
| 453 | + value = intermediateValue; |
411 | 454 | break;
|
| 455 | + } |
412 | 456 |
|
413 | 457 | context = context.parent;
|
414 | 458 | }
|
|
0 commit comments