diff --git a/FEATURES.md b/FEATURES.md index 4aceb8f0f22..2367449c356 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -44,5 +44,17 @@ for a detailed explanation. * `ember-runtime-enumerable-includes` -Deprecates `Enumerable#contains` and `Array#contains` in favor of `Enumerable#includes` and `Array#includes` +Deprecates `Enumerable#contains` and `Array#contains` in favor of `Enumerable#includes` and `Array#includes` to stay in line with ES standards (see [RFC](https://github.com/emberjs/rfcs/blob/master/text/0136-contains-to-includes.md)). + +* `ember-string-ishtmlsafe` + + Introduces an API to detect if strings are decorated as htmlSafe. Example: + + ```javascript + var plainString = 'plain string', + safeString = Ember.String.htmlSafe('
someValue
'); + + Ember.String.isHtmlSafe(plainString); // false + Ember.String.isHtmlSafe(safeString); // true + ``` diff --git a/features.json b/features.json index 38503f53568..46025e329a6 100644 --- a/features.json +++ b/features.json @@ -8,6 +8,7 @@ "ember-glimmer": null, "ember-runtime-computed-uniq-by": true, "ember-improved-instrumentation": null, - "ember-runtime-enumerable-includes": null + "ember-runtime-enumerable-includes": null, + "ember-string-ishtmlsafe": null } } diff --git a/packages/ember-glimmer/tests/compat/safe-string-test.js b/packages/ember-glimmer/tests/compat/safe-string-test.js new file mode 100644 index 00000000000..fd1b600395c --- /dev/null +++ b/packages/ember-glimmer/tests/compat/safe-string-test.js @@ -0,0 +1,32 @@ +import EmberHandlebars from 'ember-htmlbars/compat'; +import { isHtmlSafe } from 'ember-htmlbars/utils/string'; +import { TestCase } from '../utils/abstract-test-case'; +import { moduleFor } from '../utils/test-case'; + + +moduleFor('compat - SafeString', class extends TestCase { + ['@test using new results in a deprecation']() { + let result; + + expectDeprecation(() => { + result = new EmberHandlebars.SafeString('test'); + }, 'Ember.Handlebars.SafeString is deprecated in favor of Ember.String.htmlSafe'); + + this.assert.equal(result.toHTML(), 'test'); + + // Ensure this functionality is maintained for backwards compat, but also deprecated. + expectDeprecation(() => { + this.assert.ok(result instanceof EmberHandlebars.SafeString); + }, 'Ember.Handlebars.SafeString is deprecated in favor of Ember.String.htmlSafe'); + } + + ['@test isHtmlSafe should detect SafeString']() { + let safeString; + + expectDeprecation(() => { + safeString = new EmberHandlebars.SafeString('test'); + }, 'Ember.Handlebars.SafeString is deprecated in favor of Ember.String.htmlSafe'); + + this.assert.ok(isHtmlSafe(safeString)); + } +}); diff --git a/packages/ember-glimmer/tests/utils/string-test.js b/packages/ember-glimmer/tests/utils/string-test.js new file mode 100644 index 00000000000..770d79bf3fa --- /dev/null +++ b/packages/ember-glimmer/tests/utils/string-test.js @@ -0,0 +1,45 @@ +import SafeString from 'htmlbars-util/safe-string'; +import { htmlSafe, isHtmlSafe } from 'ember-htmlbars/utils/string'; +import isEnabled from 'ember-metal/features'; +import { TestCase } from './abstract-test-case'; +import { moduleFor } from './test-case'; + +moduleFor('SafeString', class extends TestCase { + ['@test htmlSafe should return an instance of SafeString']() { + let safeString = htmlSafe('you need to be more bold'); + + this.assert.ok(safeString instanceof SafeString, 'should be a SafeString'); + } + + ['@test htmlSafe should return an empty string for null']() { + let safeString = htmlSafe(null); + + this.assert.equal(safeString instanceof SafeString, true, 'should be a SafeString'); + this.assert.equal(safeString.toString(), '', 'should return an empty string'); + } + + ['@test htmlSafe should return an instance of SafeString']() { + let safeString = htmlSafe(); + + this.assert.equal(safeString instanceof SafeString, true, 'should be a SafeString'); + this.assert.equal(safeString.toString(), '', 'should return an empty string'); + } +}); + +if (isEnabled('ember-string-ishtmlsafe')) { + moduleFor('SafeString isHtmlSafe', class extends TestCase { + ['@test isHtmlSafe should detect SafeString']() { + let safeString = htmlSafe('Emphasize the important things.'); + + this.assert.ok(isHtmlSafe(safeString)); + } + + ['@test isHtmlSafe should not detect SafeString on primatives']() { + this.assert.notOk(isHtmlSafe('Hello World')); + this.assert.notOk(isHtmlSafe({})); + this.assert.notOk(isHtmlSafe([])); + this.assert.notOk(isHtmlSafe(10)); + this.assert.notOk(isHtmlSafe(null)); + } + }); +} diff --git a/packages/ember-htmlbars/lib/compat.js b/packages/ember-htmlbars/lib/compat.js index aadd0790049..457f8abfc12 100644 --- a/packages/ember-htmlbars/lib/compat.js +++ b/packages/ember-htmlbars/lib/compat.js @@ -1,12 +1,27 @@ import Ember from 'ember-metal/core'; // for Handlebars export +import { deprecate } from 'ember-metal/debug'; import { SafeString, escapeExpression } from 'ember-htmlbars/utils/string'; -const EmberHandlebars = Ember.Handlebars = Ember.Handlebars || {}; +let EmberHandlebars = Ember.Handlebars = Ember.Handlebars || {}; +Object.defineProperty(EmberHandlebars, 'SafeString', { + get() { + deprecate( + 'Ember.Handlebars.SafeString is deprecated in favor of Ember.String.htmlSafe', + false, + { + id: 'ember-htmlbars.ember-handlebars-safestring', + until: '3.0.0', + url: 'http://emberjs.com/deprecations/v2.x#toc_use-ember-string-htmlsafe-over-ember-handlebars-safestring' + } + ); + + return SafeString; + } +}); -EmberHandlebars.SafeString = SafeString; EmberHandlebars.Utils = { escapeExpression: escapeExpression }; diff --git a/packages/ember-htmlbars/lib/utils/string.js b/packages/ember-htmlbars/lib/utils/string.js index 931b20cb8ca..2ac45d4e1ce 100644 --- a/packages/ember-htmlbars/lib/utils/string.js +++ b/packages/ember-htmlbars/lib/utils/string.js @@ -6,6 +6,7 @@ import { ENV } from 'ember-environment'; import EmberStringUtils from 'ember-runtime/system/string'; import { SafeString, escapeExpression } from 'htmlbars-util'; +import isEnabled from 'ember-metal/features'; /** Mark a string as safe for unescaped output with Ember templates. If you @@ -38,8 +39,34 @@ if (ENV.EXTEND_PROTOTYPES.String) { }; } +/** + Detects if a string was decorated using `Ember.String.htmlSafe`. + + ```javascript + var plainString = 'plain string', + safeString = Ember.String.htmlSafe('
someValue
'); + + Ember.String.isHtmlSafe(plainString); // false + Ember.String.isHtmlSafe(safeString); // true + ``` + + @method isHtmlSafe + @for Ember.String + @static + @return {Boolean} `true` if the string was decorated with `htmlSafe`, `false` otherwise. + @public +*/ +function isHtmlSafe(str) { + return str && typeof str.toHTML === 'function'; +} + +if (isEnabled('ember-string-ishtmlsafe')) { + EmberStringUtils.isHtmlSafe = isHtmlSafe; +} + export { SafeString, htmlSafe, + isHtmlSafe, escapeExpression }; diff --git a/packages/ember-htmlbars/tests/compat/safe-string-test.js b/packages/ember-htmlbars/tests/compat/safe-string-test.js new file mode 120000 index 00000000000..0da6de8626a --- /dev/null +++ b/packages/ember-htmlbars/tests/compat/safe-string-test.js @@ -0,0 +1 @@ +../../../ember-glimmer/tests/compat/safe-string-test.js \ No newline at end of file diff --git a/packages/ember-htmlbars/tests/utils/string-test.js b/packages/ember-htmlbars/tests/utils/string-test.js new file mode 120000 index 00000000000..abe85d22cb5 --- /dev/null +++ b/packages/ember-htmlbars/tests/utils/string-test.js @@ -0,0 +1 @@ +../../../ember-glimmer/tests/utils/string-test.js \ No newline at end of file diff --git a/packages/ember-htmlbars/tests/utils/string_test.js b/packages/ember-htmlbars/tests/utils/string_test.js deleted file mode 100644 index 4aa0f0e533f..00000000000 --- a/packages/ember-htmlbars/tests/utils/string_test.js +++ /dev/null @@ -1,24 +0,0 @@ -import SafeString from 'htmlbars-util/safe-string'; -import { htmlSafe } from 'ember-htmlbars/utils/string'; - -QUnit.module('ember-htmlbars: SafeString'); - -QUnit.test('htmlSafe should return an instance of SafeString', function() { - let safeString = htmlSafe('you need to be more bold'); - - ok(safeString instanceof SafeString, 'should be a SafeString'); -}); - -QUnit.test('htmlSafe should return an empty string for null', function() { - let safeString = htmlSafe(null); - - equal(safeString instanceof SafeString, true, 'should be a SafeString'); - equal(safeString.toString(), '', 'should return an empty string'); -}); - -QUnit.test('htmlSafe should return an empty string for undefined', function() { - let safeString = htmlSafe(); - - equal(safeString instanceof SafeString, true, 'should be a SafeString'); - equal(safeString.toString(), '', 'should return an empty string'); -});